1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-22 01:47:00 +01:00

[RFC] add prettier to eslint (#8595)

This commit is contained in:
Brad Decker 2020-11-02 17:41:28 -06:00 committed by GitHub
parent 55bff07bbf
commit 2ebf8756a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
834 changed files with 26946 additions and 17377 deletions

View File

@ -2,19 +2,19 @@ module.exports = {
root: true,
parser: '@babel/eslint-parser',
parserOptions: {
'sourceType': 'module',
'ecmaVersion': 2017,
'ecmaFeatures': {
'experimentalObjectRestSpread': true,
'impliedStrict': true,
'modules': true,
'blockBindings': true,
'arrowFunctions': true,
'objectLiteralShorthandMethods': true,
'objectLiteralShorthandProperties': true,
'templateStrings': true,
'classes': true,
'jsx': true,
sourceType: 'module',
ecmaVersion: 2017,
ecmaFeatures: {
experimentalObjectRestSpread: true,
impliedStrict: true,
modules: true,
blockBindings: true,
arrowFunctions: true,
objectLiteralShorthandMethods: true,
objectLiteralShorthandProperties: true,
templateStrings: true,
classes: true,
jsx: true,
},
},
@ -28,6 +28,9 @@ module.exports = {
'coverage/',
'app/scripts/chromereload.js',
'app/vendor/**',
'test/e2e/send-eth-with-private-key-test/**',
'nyc_output/**',
'.vscode/**',
],
extends: [
@ -38,11 +41,7 @@ module.exports = {
'plugin:react-hooks/recommended',
],
plugins: [
'@babel',
'react',
'import',
],
plugins: ['@babel', 'react', 'import', 'prettier'],
globals: {
document: 'readonly',
@ -50,6 +49,67 @@ module.exports = {
},
rules: {
// Prettier changes and reasoning
'prettier/prettier': 'error',
// Our usage of spaces before *named* function parens is unusual, and
// doesn't match the prettier spec. prettier does not offer an option
// to configure this
'space-before-function-paren': [
'error',
{ anonymous: 'always', named: 'never' },
],
// Our eslint config has the default setting for this as error. This
// include beforeBlockComment: true, but in order to match the prettier
// spec you have to enable before and after blocks, objects and arrays
// https://github.com/prettier/eslint-config-prettier#lines-around-comment
'lines-around-comment': [
'error',
{
beforeBlockComment: true,
afterLineComment: false,
allowBlockStart: true,
allowBlockEnd: true,
allowObjectStart: true,
allowObjectEnd: true,
allowArrayStart: true,
allowArrayEnd: true,
},
],
// Prettier has some opinions on mixed-operators, and there is ongoing work
// to make the output code clear. It is better today then it was when the first
// PR to add prettier. That being said, the workaround for keeping this rule enabled
// requires breaking parts of operations into different variables -- which I believe
// to be worse. https://github.com/prettier/eslint-config-prettier#no-mixed-operators
'no-mixed-operators': 'off',
// Prettier wraps single line functions with ternaries, etc in parens by default, but
// if the line is long enough it breaks it into a separate line and removes the parens.
// The second behavior conflicts with this rule. There is some guides on the repo about
// how you can keep it enabled:
// https://github.com/prettier/eslint-config-prettier#no-confusing-arrow
// However, in practice this conflicts with prettier adding parens around short lines,
// when autofixing in vscode and others.
'no-confusing-arrow': 'off',
// There is no configuration in prettier for how it stylizes regexes, which conflicts
// with wrap-regex.
'wrap-regex': 'off',
// Prettier handles all indentation automagically. it can be configured here
// https://prettier.io/docs/en/options.html#tab-width but the default matches our
// style.
indent: 'off',
// This rule conflicts with the way that prettier breaks code across multiple lines when
// it exceeds the maximum length. Prettier optimizes for readability while simultaneously
// maximizing the amount of code per line.
'function-paren-newline': 'off',
// This rule throws an error when there is a line break in an arrow function declaration
// but prettier breaks arrow function declarations to be as readable as possible while
// still conforming to the width rules.
'implicit-arrow-linebreak': 'off',
// This rule would result in an increase in white space in lines with generator functions,
// which impacts prettier's goal of maximizing code per line and readability. There is no
// current workaround.
'generator-star-spacing': 'off',
'default-param-last': 'off',
'require-atomic-updates': 'off',
'import/no-unassigned-import': 'off',
@ -57,29 +117,32 @@ module.exports = {
'react/no-unused-prop-types': 'error',
'react/no-unused-state': 'error',
'react/jsx-boolean-value': 'error',
'react/jsx-curly-brace-presence': ['error', { 'props': 'never', 'children': 'never' }],
'react/jsx-curly-brace-presence': [
'error',
{ props: 'never', children: 'never' },
],
'react/jsx-equals-spacing': 'error',
'react/no-deprecated': 'error',
'react/default-props-match-prop-types': 'error',
'react/jsx-closing-tag-location': 'error',
'react/jsx-closing-tag-location': [
'error',
{ selfClosing: 'tag-aligned', nonEmpty: 'tag-aligned' },
],
'react/jsx-no-duplicate-props': 'error',
'react/jsx-closing-bracket-location': 'error',
'react/jsx-first-prop-new-line': ['error', 'multiline'],
'react/jsx-max-props-per-line': ['error', { 'maximum': 1, 'when': 'multiline' }],
'react/jsx-tag-spacing': ['error', {
'closingSlash': 'never',
'beforeSelfClosing': 'always',
'afterOpening': 'never',
}],
'react/jsx-wrap-multilines': ['error', {
'declaration': 'parens-new-line',
'assignment': 'parens-new-line',
'return': 'parens-new-line',
'arrow': 'parens-new-line',
'condition': 'parens-new-line',
'logical': 'parens-new-line',
'prop': 'parens-new-line',
}],
'react/jsx-max-props-per-line': [
'error',
{ maximum: 1, when: 'multiline' },
],
'react/jsx-tag-spacing': [
'error',
{
closingSlash: 'never',
beforeSelfClosing: 'always',
afterOpening: 'never',
},
],
'no-invalid-this': 'off',
'@babel/no-invalid-this': 'error',
@ -93,49 +156,40 @@ module.exports = {
'node/no-unpublished-import': 'off',
'node/no-unpublished-require': 'off',
},
overrides: [{
files: [
'test/e2e/**/*.js',
],
overrides: [
{
files: ['test/e2e/**/*.js'],
rules: {
'mocha/no-hooks-for-single-case': 'off',
},
}, {
files: [
'app/scripts/migrations/*.js',
'*.stories.js',
],
rules: {
'import/no-anonymous-default-export': ['error', { 'allowObject': true }],
},
}, {
files: [
'app/scripts/migrations/*.js',
],
{
files: ['app/scripts/migrations/*.js', '*.stories.js'],
rules: {
'import/no-anonymous-default-export': ['error', { allowObject: true }],
},
},
{
files: ['app/scripts/migrations/*.js'],
rules: {
'node/global-require': 'off',
},
}, {
files: [
'test/**/*-test.js',
'test/**/*.spec.js',
],
},
{
files: ['test/**/*-test.js', 'test/**/*.spec.js'],
rules: {
// Mocha will re-assign `this` in a test context
'@babel/no-invalid-this': 'off',
},
}, {
files: [
'development/**/*.js',
'test/e2e/benchmark.js',
'test/helper.js',
],
},
{
files: ['development/**/*.js', 'test/e2e/benchmark.js', 'test/helper.js'],
rules: {
'node/no-process-exit': 'off',
'node/shebang': 'off',
},
}, {
},
{
files: [
'.eslintrc.js',
'babel.config.js',
@ -149,11 +203,12 @@ module.exports = {
parserOptions: {
sourceType: 'script',
},
}],
},
],
settings: {
'react': {
'version': 'detect',
react: {
version: 'detect',
},
},
}

View File

@ -6,3 +6,4 @@ coverage/
app/vendor/**
.nyc_output/**
.vscode/**
test/e2e/send-eth-with-private-key-test/**

3
.prettierrc.yml Normal file
View File

@ -0,0 +1,3 @@
singleQuote: true
semi: false
trailingComma: all

View File

@ -4,7 +4,6 @@ import importers from 'ethereumjs-wallet/thirdparty'
import ethUtil from 'ethereumjs-util'
const accountImporter = {
importAccount(strategy, args) {
try {
const importer = this.strategies[strategy]
@ -43,7 +42,6 @@ const accountImporter = {
return walletToPrivateKey(wallet)
},
},
}
function walletToPrivateKey(wallet) {

View File

@ -60,9 +60,7 @@ const requestAccountTabIds = {}
// state persistence
const inTest = process.env.IN_TEST === 'true'
const localStore = inTest
? new ReadOnlyNetworkStore()
: new LocalStore()
const localStore = inTest ? new ReadOnlyNetworkStore() : new LocalStore()
let versionedData
if (inTest || process.env.METAMASK_DEBUG) {
@ -166,8 +164,8 @@ async function loadStateFromPersistence () {
// read from disk
// first from preferred, async API:
versionedData = (await localStore.get()) ||
migrator.generateInitialState(firstTimeState)
versionedData =
(await localStore.get()) || migrator.generateInitialState(firstTimeState)
// check if somehow state is empty
// this should never happen but new error reporting suggests that it has
@ -249,7 +247,9 @@ function setupController (initState, initLangCode) {
setupEnsIpfsResolver({
getCurrentNetwork: controller.getCurrentNetwork,
getIpfsGateway: controller.preferencesController.getIpfsGateway.bind(controller.preferencesController),
getIpfsGateway: controller.preferencesController.getIpfsGateway.bind(
controller.preferencesController,
),
provider: controller.provider,
})
@ -303,12 +303,14 @@ function setupController (initState, initLangCode) {
[ENVIRONMENT_TYPE_FULLSCREEN]: true,
}
const metamaskBlockedPorts = [
'trezor-connect',
]
const metamaskBlockedPorts = ['trezor-connect']
const isClientOpenStatus = () => {
return popupIsOpen || Boolean(Object.keys(openMetamaskTabsIDs).length) || notificationIsOpen
return (
popupIsOpen ||
Boolean(Object.keys(openMetamaskTabsIDs).length) ||
notificationIsOpen
)
}
/**
@ -410,12 +412,24 @@ function setupController (initState, initLangCode) {
const { unapprovedMsgCount } = controller.messageManager
const { unapprovedPersonalMsgCount } = controller.personalMessageManager
const { unapprovedDecryptMsgCount } = controller.decryptMessageManager
const { unapprovedEncryptionPublicKeyMsgCount } = controller.encryptionPublicKeyManager
const {
unapprovedEncryptionPublicKeyMsgCount,
} = controller.encryptionPublicKeyManager
const { unapprovedTypedMessagesCount } = controller.typedMessageManager
const pendingPermissionRequests = Object.keys(controller.permissionsController.permissions.state.permissionsRequests).length
const waitingForUnlockCount = controller.appStateController.waitingForUnlock.length
const count = unapprovedTxCount + unapprovedMsgCount + unapprovedPersonalMsgCount + unapprovedDecryptMsgCount + unapprovedEncryptionPublicKeyMsgCount +
unapprovedTypedMessagesCount + pendingPermissionRequests + waitingForUnlockCount
const pendingPermissionRequests = Object.keys(
controller.permissionsController.permissions.state.permissionsRequests,
).length
const waitingForUnlockCount =
controller.appStateController.waitingForUnlock.length
const count =
unapprovedTxCount +
unapprovedMsgCount +
unapprovedPersonalMsgCount +
unapprovedDecryptMsgCount +
unapprovedEncryptionPublicKeyMsgCount +
unapprovedTypedMessagesCount +
pendingPermissionRequests +
waitingForUnlockCount
if (count) {
label = String(count)
}
@ -435,7 +449,9 @@ function setupController (initState, initLangCode) {
*/
async function triggerUi() {
const tabs = await platform.getActiveTabs()
const currentlyActiveMetamaskTab = Boolean(tabs.find((tab) => openMetamaskTabsIDs[tab.id]))
const currentlyActiveMetamaskTab = Boolean(
tabs.find((tab) => openMetamaskTabsIDs[tab.id]),
)
if (!popupIsOpen && !currentlyActiveMetamaskTab) {
await notificationManager.showPopup()
}
@ -447,21 +463,22 @@ async function triggerUi () {
*/
async function openPopup() {
await triggerUi()
await new Promise(
(resolve) => {
await new Promise((resolve) => {
const interval = setInterval(() => {
if (!notificationIsOpen) {
clearInterval(interval)
resolve()
}
}, 1000)
},
)
})
}
// On first install, open a new tab with MetaMask
extension.runtime.onInstalled.addListener(({ reason }) => {
if (reason === 'install' && !(process.env.METAMASK_DEBUG || process.env.IN_TEST)) {
if (
reason === 'install' &&
!(process.env.METAMASK_DEBUG || process.env.IN_TEST)
) {
platform.openExtensionInBrowser()
}
})

View File

@ -9,7 +9,10 @@ import PortStream from 'extension-port-stream'
const fs = require('fs')
const path = require('path')
const inpageContent = fs.readFileSync(path.join(__dirname, '..', '..', 'dist', 'chrome', 'inpage.js'), 'utf8')
const inpageContent = fs.readFileSync(
path.join(__dirname, '..', '..', 'dist', 'chrome', 'inpage.js'),
'utf8',
)
const inpageSuffix = `//# sourceURL=${extension.runtime.getURL('inpage.js')}\n`
const inpageBundle = inpageContent + inpageSuffix
@ -73,17 +76,11 @@ async function setupStreams () {
const extensionMux = new ObjectMultiplex()
extensionMux.setMaxListeners(25)
pump(
pageMux,
pageStream,
pageMux,
(err) => logStreamDisconnectWarning('MetaMask Inpage Multiplex', err),
pump(pageMux, pageStream, pageMux, (err) =>
logStreamDisconnectWarning('MetaMask Inpage Multiplex', err),
)
pump(
extensionMux,
extensionStream,
extensionMux,
(err) => logStreamDisconnectWarning('MetaMask Background Multiplex', err),
pump(extensionMux, extensionStream, extensionMux, (err) =>
logStreamDisconnectWarning('MetaMask Background Multiplex', err),
)
// forward communication across inpage-background for these channels only
@ -98,11 +95,11 @@ async function setupStreams () {
function forwardTrafficBetweenMuxers(channelName, muxA, muxB) {
const channelA = muxA.createStream(channelName)
const channelB = muxB.createStream(channelName)
pump(
channelA,
channelB,
channelA,
(err) => logStreamDisconnectWarning(`MetaMask muxed traffic for channel "${channelName}" failed.`, err),
pump(channelA, channelB, channelA, (err) =>
logStreamDisconnectWarning(
`MetaMask muxed traffic for channel "${channelName}" failed.`,
err,
),
)
}
@ -126,8 +123,12 @@ function logStreamDisconnectWarning (remoteLabel, err) {
* @returns {boolean} {@code true} - if the provider should be injected
*/
function shouldInjectProvider() {
return doctypeCheck() && suffixCheck() &&
documentElementCheck() && !blockedDomainCheck()
return (
doctypeCheck() &&
suffixCheck() &&
documentElementCheck() &&
!blockedDomainCheck()
)
}
/**
@ -153,10 +154,7 @@ function doctypeCheck () {
* @returns {boolean} - whether or not the extension of the current document is prohibited
*/
function suffixCheck() {
const prohibitedTypes = [
/\.xml$/u,
/\.pdf$/u,
]
const prohibitedTypes = [/\.xml$/u, /\.pdf$/u]
const currentUrl = window.location.pathname
for (let i = 0; i < prohibitedTypes.length; i++) {
if (prohibitedTypes[i].test(currentUrl)) {
@ -201,7 +199,10 @@ function blockedDomainCheck () {
let currentRegex
for (let i = 0; i < blockedDomains.length; i++) {
const blockedDomain = blockedDomains[i].replace('.', '\\.')
currentRegex = new RegExp(`(?:https?:\\/\\/)(?:(?!${blockedDomain}).)*$`, 'u')
currentRegex = new RegExp(
`(?:https?:\\/\\/)(?:(?!${blockedDomain}).)*$`,
'u',
)
if (!currentRegex.test(currentUrl)) {
return true
}
@ -230,5 +231,7 @@ async function domIsReady () {
return undefined
}
// wait for load
return new Promise((resolve) => window.addEventListener('DOMContentLoaded', resolve, { once: true }))
return new Promise((resolve) =>
window.addEventListener('DOMContentLoaded', resolve, { once: true }),
)
}

View File

@ -21,8 +21,7 @@ export const ALERT_TYPES = {
}
const defaultState = {
alertEnabledness: Object.keys(ALERT_TYPES)
.reduce(
alertEnabledness: Object.keys(ALERT_TYPES).reduce(
(alertEnabledness, alertType) => {
alertEnabledness[alertType] = true
return alertEnabledness
@ -37,7 +36,6 @@ const defaultState = {
* alert related state
*/
export default class AlertController {
/**
* @constructor
* @param {AlertControllerOptions} [opts] - Controller configuration parameters
@ -56,7 +54,10 @@ export default class AlertController {
preferencesStore.subscribe(({ selectedAddress }) => {
const currentState = this.store.getState()
if (currentState.unconnectedAccountAlertShownOrigins && this.selectedAddress !== selectedAddress) {
if (
currentState.unconnectedAccountAlertShownOrigins &&
this.selectedAddress !== selectedAddress
) {
this.selectedAddress = selectedAddress
this.store.updateState({ unconnectedAccountAlertShownOrigins: {} })
}
@ -76,7 +77,9 @@ export default class AlertController {
*/
setUnconnectedAccountAlertShown(origin) {
let { unconnectedAccountAlertShownOrigins } = this.store.getState()
unconnectedAccountAlertShownOrigins = { ...unconnectedAccountAlertShownOrigins }
unconnectedAccountAlertShownOrigins = {
...unconnectedAccountAlertShownOrigins,
}
unconnectedAccountAlertShownOrigins[origin] = true
this.store.updateState({ unconnectedAccountAlertShownOrigins })
}

View File

@ -2,7 +2,6 @@ import EventEmitter from 'events'
import ObservableStore from 'obs-store'
export default class AppStateController extends EventEmitter {
/**
* @constructor
* @param opts
@ -23,7 +22,8 @@ export default class AppStateController extends EventEmitter {
timeoutMinutes: 0,
connectedStatusPopoverHasBeenShown: true,
swapsWelcomeMessageHasBeenShown: false,
defaultHomeActiveTabName: null, ...initState,
defaultHomeActiveTabName: null,
...initState,
})
this.timer = null
@ -162,6 +162,9 @@ export default class AppStateController extends EventEmitter {
return
}
this.timer = setTimeout(() => this.onInactiveTimeout(), timeoutMinutes * 60 * 1000)
this.timer = setTimeout(
() => this.onInactiveTimeout(),
timeoutMinutes * 60 * 1000,
)
}
}

View File

@ -12,7 +12,6 @@ import ObservableStore from 'obs-store'
* a cache of account balances in local storage
*/
export default class CachedBalancesController {
/**
* Creates a new controller instance
*
@ -39,7 +38,10 @@ export default class CachedBalancesController {
*/
async updateCachedBalances({ accounts }) {
const network = await this.getNetwork()
const balancesToCache = await this._generateBalancesToCache(accounts, network)
const balancesToCache = await this._generateBalancesToCache(
accounts,
network,
)
this.store.updateState({
cachedBalances: balancesToCache,
})

View File

@ -6,20 +6,25 @@ import { MAINNET } from './network/enums'
// By default, poll every 3 minutes
const DEFAULT_INTERVAL = 180 * 1000
const SINGLE_CALL_BALANCES_ADDRESS = '0xb1f8e55c7f64d203c1400b9d8555d050f94adf39'
const SINGLE_CALL_BALANCES_ADDRESS =
'0xb1f8e55c7f64d203c1400b9d8555d050f94adf39'
/**
* A controller that polls for token exchange
* rates based on a user's current token list
*/
export default class DetectTokensController {
/**
* Creates a DetectTokensController
*
* @param {Object} [config] - Options to configure controller
*/
constructor ({ interval = DEFAULT_INTERVAL, preferences, network, keyringMemStore } = {}) {
constructor({
interval = DEFAULT_INTERVAL,
preferences,
network,
keyringMemStore,
} = {}) {
this.preferences = preferences
this.interval = interval
this.network = network
@ -40,7 +45,10 @@ export default class DetectTokensController {
const tokensToDetect = []
this.web3.setProvider(this._network._provider)
for (const contractAddress in contracts) {
if (contracts[contractAddress].erc20 && !(this.tokenAddresses.includes(contractAddress.toLowerCase()))) {
if (
contracts[contractAddress].erc20 &&
!this.tokenAddresses.includes(contractAddress.toLowerCase())
) {
tokensToDetect.push(contractAddress)
}
}
@ -49,20 +57,29 @@ export default class DetectTokensController {
try {
result = await this._getTokenBalances(tokensToDetect)
} catch (error) {
warn(`MetaMask - DetectTokensController single call balance fetch failed`, error)
warn(
`MetaMask - DetectTokensController single call balance fetch failed`,
error,
)
return
}
tokensToDetect.forEach((tokenAddress, index) => {
const balance = result[index]
if (balance && !balance.isZero()) {
this._preferences.addToken(tokenAddress, contracts[tokenAddress].symbol, contracts[tokenAddress].decimals)
this._preferences.addToken(
tokenAddress,
contracts[tokenAddress].symbol,
contracts[tokenAddress].decimals,
)
}
})
}
async _getTokenBalances(tokens) {
const ethContract = this.web3.eth.contract(SINGLE_CALL_BALANCES_ABI).at(SINGLE_CALL_BALANCES_ADDRESS)
const ethContract = this.web3.eth
.contract(SINGLE_CALL_BALANCES_ABI)
.at(SINGLE_CALL_BALANCES_ADDRESS)
return new Promise((resolve, reject) => {
ethContract.balances([this.selectedAddress], tokens, (error, result) => {
if (error) {

View File

@ -68,7 +68,10 @@ export default class EnsController {
return undefined
}
if (registeredAddress === ZERO_ADDRESS || registeredAddress === ZERO_X_ERROR_ADDRESS) {
if (
registeredAddress === ZERO_ADDRESS ||
registeredAddress === ZERO_X_ERROR_ADDRESS
) {
return undefined
}

View File

@ -40,13 +40,8 @@ const etherscanSupportedNetworks = [
]
export default class IncomingTransactionsController {
constructor(opts = {}) {
const {
blockTracker,
networkController,
preferencesController,
} = opts
const { blockTracker, networkController, preferencesController } = opts
this.blockTracker = blockTracker
this.networkController = networkController
this.preferencesController = preferencesController
@ -68,13 +63,23 @@ export default class IncomingTransactionsController {
[MAINNET]: null,
[RINKEBY]: null,
[ROPSTEN]: null,
}, ...opts.initState,
},
...opts.initState,
}
this.store = new ObservableStore(initState)
this.preferencesController.store.subscribe(pairwise((prevState, currState) => {
const { featureFlags: { showIncomingTransactions: prevShowIncomingTransactions } = {} } = prevState
const { featureFlags: { showIncomingTransactions: currShowIncomingTransactions } = {} } = currState
this.preferencesController.store.subscribe(
pairwise((prevState, currState) => {
const {
featureFlags: {
showIncomingTransactions: prevShowIncomingTransactions,
} = {},
} = prevState
const {
featureFlags: {
showIncomingTransactions: currShowIncomingTransactions,
} = {},
} = currState
if (currShowIncomingTransactions === prevShowIncomingTransactions) {
return
@ -86,9 +91,11 @@ export default class IncomingTransactionsController {
}
this.start()
}))
}),
)
this.preferencesController.store.subscribe(pairwise(async (prevState, currState) => {
this.preferencesController.store.subscribe(
pairwise(async (prevState, currState) => {
const { selectedAddress: prevSelectedAddress } = prevState
const { selectedAddress: currSelectedAddress } = currState
@ -99,7 +106,8 @@ export default class IncomingTransactionsController {
await this._update({
address: currSelectedAddress,
})
}))
}),
)
this.networkController.on('networkDidChange', async () => {
const address = this.preferencesController.getSelectedAddress()
@ -131,7 +139,11 @@ export default class IncomingTransactionsController {
return
}
try {
const dataForUpdate = await this._getDataForUpdate({ address, chainId, newBlockNumberDec })
const dataForUpdate = await this._getDataForUpdate({
address,
chainId,
newBlockNumberDec,
})
this._updateStateWithNewTxData(dataForUpdate)
} catch (err) {
log.error(err)
@ -144,13 +156,18 @@ export default class IncomingTransactionsController {
incomingTxLastFetchedBlocksByNetwork: currentBlocksByNetwork,
} = this.store.getState()
const lastFetchBlockByCurrentNetwork = currentBlocksByNetwork[CHAIN_ID_TO_TYPE_MAP[chainId]]
const lastFetchBlockByCurrentNetwork =
currentBlocksByNetwork[CHAIN_ID_TO_TYPE_MAP[chainId]]
let blockToFetchFrom = lastFetchBlockByCurrentNetwork || newBlockNumberDec
if (blockToFetchFrom === undefined) {
blockToFetchFrom = parseInt(this.blockTracker.getCurrentBlock(), 16)
}
const { latestIncomingTxBlockNumber, txs: newTxs } = await this._fetchAll(address, blockToFetchFrom, chainId)
const { latestIncomingTxBlockNumber, txs: newTxs } = await this._fetchAll(
address,
blockToFetchFrom,
chainId,
)
return {
latestIncomingTxBlockNumber,
@ -195,7 +212,8 @@ export default class IncomingTransactionsController {
}
async _fetchTxs(address, fromBlock, chainId) {
const etherscanSubdomain = chainId === MAINNET_CHAIN_ID
const etherscanSubdomain =
chainId === MAINNET_CHAIN_ID
? 'api'
: `api-${CHAIN_ID_TO_TYPE_MAP[chainId]}`
@ -226,7 +244,11 @@ export default class IncomingTransactionsController {
}
})
const incomingTxs = remoteTxs.filter((tx) => tx.txParams.to && tx.txParams.to.toLowerCase() === address.toLowerCase())
const incomingTxs = remoteTxs.filter(
(tx) =>
tx.txParams.to &&
tx.txParams.to.toLowerCase() === address.toLowerCase(),
)
incomingTxs.sort((a, b) => (a.time < b.time ? -1 : 1))
let latestIncomingTxBlockNumber = null
@ -234,7 +256,8 @@ export default class IncomingTransactionsController {
if (
tx.blockNumber &&
(!latestIncomingTxBlockNumber ||
parseInt(latestIncomingTxBlockNumber, 10) < parseInt(tx.blockNumber, 10))
parseInt(latestIncomingTxBlockNumber, 10) <
parseInt(tx.blockNumber, 10))
) {
latestIncomingTxBlockNumber = tx.blockNumber
}

View File

@ -1,4 +1,8 @@
export const SINGLE_CALL_BALANCES_ADDRESS = '0xb1f8e55c7f64d203c1400b9d8555d050f94adf39'
export const SINGLE_CALL_BALANCES_ADDRESS_RINKEBY = '0x9f510b19f1ad66f0dcf6e45559fab0d6752c1db7'
export const SINGLE_CALL_BALANCES_ADDRESS_ROPSTEN = '0xb8e671734ce5c8d7dfbbea5574fa4cf39f7a54a4'
export const SINGLE_CALL_BALANCES_ADDRESS_KOVAN = '0xb1d3fbb2f83aecd196f474c16ca5d9cffa0d0ffc'
export const SINGLE_CALL_BALANCES_ADDRESS =
'0xb1f8e55c7f64d203c1400b9d8555d050f94adf39'
export const SINGLE_CALL_BALANCES_ADDRESS_RINKEBY =
'0x9f510b19f1ad66f0dcf6e45559fab0d6752c1db7'
export const SINGLE_CALL_BALANCES_ADDRESS_ROPSTEN =
'0xb8e671734ce5c8d7dfbbea5574fa4cf39f7a54a4'
export const SINGLE_CALL_BALANCES_ADDRESS_KOVAN =
'0xb1d3fbb2f83aecd196f474c16ca5d9cffa0d0ffc'

View File

@ -9,13 +9,9 @@ import providerFromMiddleware from 'eth-json-rpc-middleware/providerFromMiddlewa
import BlockTracker from 'eth-block-tracker'
const inTest = process.env.IN_TEST === 'true'
const blockTrackerOpts = inTest
? { pollingInterval: 1000 }
: {}
const blockTrackerOpts = inTest ? { pollingInterval: 1000 } : {}
const getTestMiddlewares = () => {
return inTest
? [createEstimateGasDelayTestMiddleware()]
: []
return inTest ? [createEstimateGasDelayTestMiddleware()] : []
}
export default function createJsonRpcClient({ rpcUrl, chainId }) {

View File

@ -1,7 +1,10 @@
import mergeMiddleware from 'json-rpc-engine/src/mergeMiddleware'
import createScaffoldMiddleware from 'json-rpc-engine/src/createScaffoldMiddleware'
import createWalletSubprovider from 'eth-json-rpc-middleware/wallet'
import { createPendingNonceMiddleware, createPendingTxMiddleware } from './middleware/pending'
import {
createPendingNonceMiddleware,
createPendingTxMiddleware,
} from './middleware/pending'
export default function createMetamaskMiddleware({
version,

View File

@ -22,13 +22,7 @@ export const KOVAN_DISPLAY_NAME = 'Kovan'
export const MAINNET_DISPLAY_NAME = 'Ethereum Mainnet'
export const GOERLI_DISPLAY_NAME = 'Goerli'
export const INFURA_PROVIDER_TYPES = [
ROPSTEN,
RINKEBY,
KOVAN,
MAINNET,
GOERLI,
]
export const INFURA_PROVIDER_TYPES = [ROPSTEN, RINKEBY, KOVAN, MAINNET, GOERLI]
export const NETWORK_TYPE_TO_ID_MAP = {
[ROPSTEN]: { networkId: ROPSTEN_NETWORK_ID, chainId: ROPSTEN_CHAIN_ID },
@ -58,20 +52,16 @@ export const NETWORK_TO_NAME_MAP = {
[MAINNET_CHAIN_ID]: MAINNET_DISPLAY_NAME,
}
export const CHAIN_ID_TO_TYPE_MAP = Object.entries(NETWORK_TYPE_TO_ID_MAP)
.reduce(
(chainIdToTypeMap, [networkType, { chainId }]) => {
export const CHAIN_ID_TO_TYPE_MAP = Object.entries(
NETWORK_TYPE_TO_ID_MAP,
).reduce((chainIdToTypeMap, [networkType, { chainId }]) => {
chainIdToTypeMap[chainId] = networkType
return chainIdToTypeMap
},
{},
)
}, {})
export const CHAIN_ID_TO_NETWORK_ID_MAP = Object.values(NETWORK_TYPE_TO_ID_MAP)
.reduce(
(chainIdToNetworkIdMap, { chainId, networkId }) => {
export const CHAIN_ID_TO_NETWORK_ID_MAP = Object.values(
NETWORK_TYPE_TO_ID_MAP,
).reduce((chainIdToNetworkIdMap, { chainId, networkId }) => {
chainIdToNetworkIdMap[chainId] = networkId
return chainIdToNetworkIdMap
},
{},
)
}, {})

View File

@ -5,7 +5,10 @@ import ComposedStore from 'obs-store/lib/composed'
import JsonRpcEngine from 'json-rpc-engine'
import providerFromEngine from 'eth-json-rpc-middleware/providerFromEngine'
import log from 'loglevel'
import { createSwappableProxy, createEventEmitterProxy } from 'swappable-obj-proxy'
import {
createSwappableProxy,
createEventEmitterProxy,
} from 'swappable-obj-proxy'
import EthQuery from 'eth-query'
import createMetamaskMiddleware from './createMetamaskMiddleware'
import createInfuraClient from './createInfuraClient'
@ -40,7 +43,6 @@ const defaultProviderConfig = {
}
export default class NetworkController extends EventEmitter {
constructor(opts = {}) {
super()
@ -116,13 +118,17 @@ export default class NetworkController extends EventEmitter {
lookupNetwork() {
// Prevent firing when provider is not defined.
if (!this._provider) {
log.warn('NetworkController - lookupNetwork aborted due to missing provider')
log.warn(
'NetworkController - lookupNetwork aborted due to missing provider',
)
return
}
const chainId = this.getCurrentChainId()
if (!chainId) {
log.warn('NetworkController - lookupNetwork aborted due to missing chainId')
log.warn(
'NetworkController - lookupNetwork aborted due to missing chainId',
)
this.setNetworkState('loading')
return
}
@ -160,8 +166,15 @@ export default class NetworkController extends EventEmitter {
}
async setProviderType(type, rpcUrl = '', ticker = 'ETH', nickname = '') {
assert.notEqual(type, 'rpc', `NetworkController - cannot call "setProviderType" with type 'rpc'. use "setRpcTarget"`)
assert(INFURA_PROVIDER_TYPES.includes(type), `NetworkController - Unknown rpc type "${type}"`)
assert.notEqual(
type,
'rpc',
`NetworkController - cannot call "setProviderType" with type 'rpc'. use "setRpcTarget"`,
)
assert(
INFURA_PROVIDER_TYPES.includes(type),
`NetworkController - Unknown rpc type "${type}"`,
)
const { chainId } = NETWORK_TYPE_TO_ID_MAP[type]
this.setProviderConfig({ type, rpcUrl, chainId, ticker, nickname })
}
@ -201,7 +214,9 @@ export default class NetworkController extends EventEmitter {
} else if (type === 'rpc') {
this._configureStandardProvider(rpcUrl, chainId)
} else {
throw new Error(`NetworkController - _configureProvider - unknown type "${type}"`)
throw new Error(
`NetworkController - _configureProvider - unknown type "${type}"`,
)
}
}
@ -221,7 +236,9 @@ export default class NetworkController extends EventEmitter {
}
_setNetworkClient({ networkMiddleware, blockTracker }) {
const metamaskMiddleware = createMetamaskMiddleware(this._baseProviderParams)
const metamaskMiddleware = createMetamaskMiddleware(
this._baseProviderParams,
)
const engine = new JsonRpcEngine()
engine.push(metamaskMiddleware)
engine.push(networkMiddleware)
@ -239,7 +256,9 @@ export default class NetworkController extends EventEmitter {
if (this._blockTrackerProxy) {
this._blockTrackerProxy.setTarget(blockTracker)
} else {
this._blockTrackerProxy = createEventEmitterProxy(blockTracker, { eventFilter: 'skipInternal' })
this._blockTrackerProxy = createEventEmitterProxy(blockTracker, {
eventFilter: 'skipInternal',
})
}
// set new provider and blockTracker
this._provider = provider

View File

@ -4,19 +4,21 @@ export const getNetworkDisplayName = (key) => NETWORK_TO_NAME_MAP[key]
export function formatTxMetaForRpcResult(txMeta) {
return {
'blockHash': txMeta.txReceipt ? txMeta.txReceipt.blockHash : null,
'blockNumber': txMeta.txReceipt ? txMeta.txReceipt.blockNumber : null,
'from': txMeta.txParams.from,
'gas': txMeta.txParams.gas,
'gasPrice': txMeta.txParams.gasPrice,
'hash': txMeta.hash,
'input': txMeta.txParams.data || '0x',
'nonce': txMeta.txParams.nonce,
'to': txMeta.txParams.to,
'transactionIndex': txMeta.txReceipt ? txMeta.txReceipt.transactionIndex : null,
'value': txMeta.txParams.value || '0x0',
'v': txMeta.v,
'r': txMeta.r,
's': txMeta.s,
blockHash: txMeta.txReceipt ? txMeta.txReceipt.blockHash : null,
blockNumber: txMeta.txReceipt ? txMeta.txReceipt.blockNumber : null,
from: txMeta.txParams.from,
gas: txMeta.txParams.gas,
gasPrice: txMeta.txParams.gasPrice,
hash: txMeta.hash,
input: txMeta.txParams.data || '0x',
nonce: txMeta.txParams.nonce,
to: txMeta.txParams.to,
transactionIndex: txMeta.txReceipt
? txMeta.txReceipt.transactionIndex
: null,
value: txMeta.txParams.value || '0x0',
v: txMeta.v,
r: txMeta.r,
s: txMeta.s,
}
}

View File

@ -17,7 +17,6 @@ import log from 'loglevel'
* state related to onboarding
*/
export default class OnboardingController {
/**
* Creates a new controller instance
*
@ -65,7 +64,9 @@ export default class OnboardingController {
}
const onboardingTabs = { ...this.store.getState().onboardingTabs }
if (!onboardingTabs[location] || onboardingTabs[location] !== tabId) {
log.debug(`Registering onboarding tab at location '${location}' with tabId '${tabId}'`)
log.debug(
`Registering onboarding tab at location '${location}' with tabId '${tabId}'`,
)
onboardingTabs[location] = tabId
this.store.updateState({ onboardingTabs })
}

View File

@ -1,4 +1,3 @@
export const WALLET_PREFIX = 'wallet_'
export const HISTORY_STORE_KEY = 'permissionsHistory'
@ -23,9 +22,7 @@ export const NOTIFICATION_NAMES = {
accountsChanged: 'wallet_accountsChanged',
}
export const LOG_IGNORE_METHODS = [
'wallet_sendDomainMetadata',
]
export const LOG_IGNORE_METHODS = ['wallet_sendDomainMetadata']
export const LOG_METHOD_TYPES = {
restricted: 'restricted',

View File

@ -24,7 +24,6 @@ import {
} from './enums'
export class PermissionsController {
constructor(
{
getKeyringAccounts,
@ -38,7 +37,6 @@ export class PermissionsController {
restoredPermissions = {},
restoredState = {},
) {
// additional top-level store key set in _initializeMetadataStore
this.store = new ObservableStore({
[LOG_STORE_KEY]: restoredState[LOG_STORE_KEY] || [],
@ -76,7 +74,6 @@ export class PermissionsController {
}
createMiddleware({ origin, extensionId }) {
if (typeof origin !== 'string' || !origin.length) {
throw new Error('Must provide non-empty string origin.')
}
@ -91,20 +88,26 @@ export class PermissionsController {
engine.push(this.permissionsLog.createMiddleware())
engine.push(createPermissionsMethodMiddleware({
engine.push(
createPermissionsMethodMiddleware({
addDomainMetadata: this.addDomainMetadata.bind(this),
getAccounts: this.getAccounts.bind(this, origin),
getUnlockPromise: () => this._getUnlockPromise(true),
hasPermission: this.hasPermission.bind(this, origin),
notifyAccountsChanged: this.notifyAccountsChanged.bind(this, origin),
requestAccountsPermission: this._requestPermissions.bind(
this, { origin }, { eth_accounts: {} },
this,
{ origin },
{ eth_accounts: {} },
),
}))
}),
)
engine.push(this.permissions.providerMiddlewareFunction.bind(
this.permissions, { origin },
))
engine.push(
this.permissions.providerMiddlewareFunction.bind(this.permissions, {
origin,
}),
)
return asMiddleware(engine)
}
@ -129,11 +132,14 @@ export class PermissionsController {
*/
getAccounts(origin) {
return new Promise((resolve, _) => {
const req = { method: 'eth_accounts' }
const res = {}
this.permissions.providerMiddlewareFunction(
{ origin }, req, res, () => undefined, _end,
{ origin },
req,
res,
() => undefined,
_end,
)
function _end() {
@ -177,7 +183,6 @@ export class PermissionsController {
*/
_requestPermissions(domain, permissions, id) {
return new Promise((resolve, reject) => {
// rpc-cap assigns an id to the request if there is none, as expected by
// requestUserApproval below
const req = {
@ -188,7 +193,11 @@ export class PermissionsController {
const res = {}
this.permissions.providerMiddlewareFunction(
domain, req, res, () => undefined, _end,
domain,
req,
res,
() => undefined,
_end,
)
function _end(_err) {
@ -212,7 +221,6 @@ export class PermissionsController {
* @param {Array} accounts - The accounts to expose, if any
*/
async approvePermissionsRequest(approved, accounts) {
const { id } = approved.metadata
const approval = this.pendingApprovals.get(id)
@ -222,28 +230,29 @@ export class PermissionsController {
}
try {
if (Object.keys(approved.permissions).length === 0) {
approval.reject(ethErrors.rpc.invalidRequest({
approval.reject(
ethErrors.rpc.invalidRequest({
message: 'Must request at least one permission.',
}))
}),
)
} else {
// attempt to finalize the request and resolve it,
// settings caveats as necessary
approved.permissions = await this.finalizePermissionsRequest(
approved.permissions, accounts,
approved.permissions,
accounts,
)
approval.resolve(approved.permissions)
}
} catch (err) {
// if finalization fails, reject the request
approval.reject(ethErrors.rpc.invalidRequest({
message: err.message, data: err,
}))
approval.reject(
ethErrors.rpc.invalidRequest({
message: err.message,
data: err,
}),
)
}
this._removePendingApproval(id)
@ -278,7 +287,6 @@ export class PermissionsController {
* @param {string} account - The new account to expose.
*/
async addPermittedAccount(origin, account) {
const domains = this.permissions.getDomains()
if (!domains[origin]) {
throw new Error('Unrecognized domain')
@ -294,7 +302,8 @@ export class PermissionsController {
}
this.permissions.updateCaveatFor(
origin, 'eth_accounts',
origin,
'eth_accounts',
CAVEAT_NAMES.exposedAccounts,
[...oldPermittedAccounts, account],
)
@ -316,7 +325,6 @@ export class PermissionsController {
* @param {string} account - The account to remove.
*/
async removePermittedAccount(origin, account) {
const domains = this.permissions.getDomains()
if (!domains[origin]) {
throw new Error('Unrecognized domain')
@ -331,15 +339,16 @@ export class PermissionsController {
throw new Error('Account is not permitted for origin')
}
let newPermittedAccounts = oldPermittedAccounts
.filter((acc) => acc !== account)
let newPermittedAccounts = oldPermittedAccounts.filter(
(acc) => acc !== account,
)
if (newPermittedAccounts.length === 0) {
this.removePermissionsFor({ [origin]: ['eth_accounts'] })
} else {
this.permissions.updateCaveatFor(
origin, 'eth_accounts',
origin,
'eth_accounts',
CAVEAT_NAMES.exposedAccounts,
newPermittedAccounts,
)
@ -362,10 +371,15 @@ export class PermissionsController {
this.validatePermittedAccounts([account])
const domains = this.permissions.getDomains()
const connectedOrigins = Object.keys(domains)
.filter((origin) => this._getPermittedAccounts(origin).includes(account))
const connectedOrigins = Object.keys(domains).filter((origin) =>
this._getPermittedAccounts(origin).includes(account),
)
await Promise.all(connectedOrigins.map((origin) => this.removePermittedAccount(origin, account)))
await Promise.all(
connectedOrigins.map((origin) =>
this.removePermittedAccount(origin, account),
),
)
}
/**
@ -379,14 +393,12 @@ export class PermissionsController {
* @returns {Object} The finalized permissions request object.
*/
async finalizePermissionsRequest(requestedPermissions, requestedAccounts) {
const finalizedPermissions = cloneDeep(requestedPermissions)
const finalizedAccounts = cloneDeep(requestedAccounts)
const { eth_accounts: ethAccounts } = finalizedPermissions
if (ethAccounts) {
this.validatePermittedAccounts(finalizedAccounts)
if (!ethAccounts.caveats) {
@ -394,9 +406,11 @@ export class PermissionsController {
}
// caveat names are unique, and we will only construct this caveat here
ethAccounts.caveats = ethAccounts.caveats.filter((c) => (
c.name !== CAVEAT_NAMES.exposedAccounts && c.name !== CAVEAT_NAMES.primaryAccountOnly
))
ethAccounts.caveats = ethAccounts.caveats.filter(
(c) =>
c.name !== CAVEAT_NAMES.exposedAccounts &&
c.name !== CAVEAT_NAMES.primaryAccountOnly,
)
ethAccounts.caveats.push({
type: CAVEAT_TYPES.limitResponseLength,
@ -442,7 +456,6 @@ export class PermissionsController {
* @param {Array<string>} newAccounts - The currently permitted accounts.
*/
notifyAccountsChanged(origin, newAccounts) {
if (typeof origin !== 'string' || !origin) {
throw new Error(`Invalid origin: '${origin}'`)
}
@ -459,9 +472,7 @@ export class PermissionsController {
// if the accounts changed from the perspective of the dapp,
// update "last seen" time for the origin and account(s)
// exception: no accounts -> no times to update
this.permissionsLog.updateAccountsHistory(
origin, newAccounts,
)
this.permissionsLog.updateAccountsHistory(origin, newAccounts)
// NOTE:
// we don't check for accounts changing in the notifyAllDomains case,
@ -479,13 +490,10 @@ export class PermissionsController {
* origins to permissions to remove.
*/
removePermissionsFor(domains) {
Object.entries(domains).forEach(([origin, perms]) => {
this.permissions.removePermissionsFor(
origin,
perms.map((methodName) => {
if (methodName === 'eth_accounts') {
this.notifyAccountsChanged(origin, [])
}
@ -518,7 +526,6 @@ export class PermissionsController {
* @param {Object} metadata - The domain's metadata that will be stored.
*/
addDomainMetadata(origin, metadata) {
const oldMetadataState = this.store.getState()[METADATA_STORE_KEY]
const newMetadataState = { ...oldMetadataState }
@ -541,7 +548,10 @@ export class PermissionsController {
lastUpdated: Date.now(),
}
if (!newMetadataState[origin].extensionId && !newMetadataState[origin].host) {
if (
!newMetadataState[origin].extensionId &&
!newMetadataState[origin].host
) {
newMetadataState[origin].host = new URL(origin).host
}
@ -558,7 +568,6 @@ export class PermissionsController {
* @param {Object} restoredState - The restored permissions controller state.
*/
_initializeMetadataStore(restoredState) {
const metadataState = restoredState[METADATA_STORE_KEY] || {}
const newMetadataState = this._trimDomainMetadata(metadataState)
@ -575,7 +584,6 @@ export class PermissionsController {
* @returns {Object} The new metadata state object.
*/
_trimDomainMetadata(metadataState) {
const newMetadataState = { ...metadataState }
const origins = Object.keys(metadataState)
const permissionsDomains = this.permissions.getDomains()
@ -606,8 +614,7 @@ export class PermissionsController {
_getPermittedAccounts(origin) {
const permittedAccounts = this.permissions
.getPermission(origin, 'eth_accounts')
?.caveats
?.find((caveat) => caveat.name === CAVEAT_NAMES.exposedAccounts)
?.caveats?.find((caveat) => caveat.name === CAVEAT_NAMES.exposedAccounts)
?.value
return permittedAccounts || null
@ -623,7 +630,6 @@ export class PermissionsController {
* @param {string} account - The newly selected account's address.
*/
async _handleAccountSelected(account) {
if (typeof account !== 'string') {
throw new Error('Selected account should be a non-empty string.')
}
@ -631,19 +637,19 @@ export class PermissionsController {
const domains = this.permissions.getDomains() || {}
const connectedDomains = Object.entries(domains)
.filter(([_, { permissions }]) => {
const ethAccounts = permissions.find((permission) => permission.parentCapability === 'eth_accounts')
const exposedAccounts = ethAccounts
?.caveats
.find((caveat) => caveat.name === 'exposedAccounts')
?.value
const ethAccounts = permissions.find(
(permission) => permission.parentCapability === 'eth_accounts',
)
const exposedAccounts = ethAccounts?.caveats.find(
(caveat) => caveat.name === 'exposedAccounts',
)?.value
return exposedAccounts?.includes(account)
})
.map(([domain]) => domain)
await Promise.all(
connectedDomains
.map(
(origin) => this._handleConnectedAccountSelected(origin),
connectedDomains.map((origin) =>
this._handleConnectedAccountSelected(origin),
),
)
}
@ -670,7 +676,6 @@ export class PermissionsController {
* @param {Function} reject - The function rejecting the pending approval Promise.
*/
_addPendingApproval(id, origin, resolve, reject) {
if (
this.pendingApprovalOrigins.has(origin) ||
this.pendingApprovals.has(id)
@ -701,12 +706,11 @@ export class PermissionsController {
* @param {string} origin = The origin string representing the domain.
*/
_initializePermissions(restoredState) {
// these permission requests are almost certainly stale
const initState = { ...restoredState, permissionsRequests: [] }
this.permissions = new RpcCap({
this.permissions = new RpcCap(
{
// Supports passthrough methods:
safeMethods: SAFE_METHODS,
@ -725,7 +729,9 @@ export class PermissionsController {
* @param {string} req - The internal rpc-cap user request object.
*/
requestUserApproval: async (req) => {
const { metadata: { id, origin } } = req
const {
metadata: { id, origin },
} = req
if (this.pendingApprovalOrigins.has(origin)) {
throw ethErrors.rpc.resourceUnavailable(
@ -739,7 +745,9 @@ export class PermissionsController {
this._addPendingApproval(id, origin, resolve, reject)
})
},
}, initState)
},
initState,
)
}
}

View File

@ -14,7 +14,6 @@ import {
* and permissions-related methods.
*/
export default class PermissionsLogController {
constructor({ restrictedMethods, store }) {
this.restrictedMethods = restrictedMethods
this.store = store
@ -64,7 +63,6 @@ export default class PermissionsLogController {
* @param {Array<string>} accounts - The accounts.
*/
updateAccountsHistory(origin, accounts) {
if (accounts.length === 0) {
return
}
@ -90,7 +88,6 @@ export default class PermissionsLogController {
*/
createMiddleware() {
return (req, res, next, _end) => {
let activityEntry, requestedMethods
const { origin, method } = req
const isInternal = method.startsWith(WALLET_PREFIX)
@ -100,7 +97,6 @@ export default class PermissionsLogController {
!LOG_IGNORE_METHODS.includes(method) &&
(isInternal || this.restrictedMethods.includes(method))
) {
activityEntry = this.logRequest(req, isInternal)
if (method === `${WALLET_PREFIX}requestPermissions`) {
@ -109,7 +105,6 @@ export default class PermissionsLogController {
requestedMethods = this.getRequestedMethods(req)
}
} else if (method === 'eth_requestAccounts') {
// eth_requestAccounts is a special case; we need to extract the accounts
// from it
activityEntry = this.logRequest(req, isInternal)
@ -122,7 +117,6 @@ export default class PermissionsLogController {
// call next with a return handler for capturing the response
next((cb) => {
const time = Date.now()
this.logResponse(activityEntry, res, time)
@ -130,7 +124,10 @@ export default class PermissionsLogController {
// any permissions or accounts changes will be recorded on the response,
// so we only log permissions history here
this.logPermissionsHistory(
requestedMethods, origin, res.result, time,
requestedMethods,
origin,
res.result,
time,
method === 'eth_requestAccounts',
)
}
@ -149,9 +146,9 @@ export default class PermissionsLogController {
const activityEntry = {
id: request.id,
method: request.method,
methodType: (
isInternal ? LOG_METHOD_TYPES.internal : LOG_METHOD_TYPES.restricted
),
methodType: isInternal
? LOG_METHOD_TYPES.internal
: LOG_METHOD_TYPES.restricted,
origin: request.origin,
request: cloneDeep(request),
requestTime: Date.now(),
@ -172,7 +169,6 @@ export default class PermissionsLogController {
* @param {number} time - Output from Date.now()
*/
logResponse(entry, response, time) {
if (!entry || !response) {
return
}
@ -189,7 +185,6 @@ export default class PermissionsLogController {
* @param {Object} entry - The activity log entry.
*/
commitNewActivity(entry) {
const logs = this.getActivityLog()
// add new entry to end of log
@ -213,31 +208,30 @@ export default class PermissionsLogController {
* @param {boolean} isEthRequestAccounts - Whether the permissions request was 'eth_requestAccounts'.
*/
logPermissionsHistory(
requestedMethods, origin, result,
time, isEthRequestAccounts,
requestedMethods,
origin,
result,
time,
isEthRequestAccounts,
) {
let accounts, newEntries
if (isEthRequestAccounts) {
accounts = result
const accountToTimeMap = getAccountToTimeMap(accounts, time)
newEntries = {
'eth_accounts': {
eth_accounts: {
accounts: accountToTimeMap,
lastApproved: time,
},
}
} else {
// Records new "lastApproved" times for the granted permissions, if any.
// Special handling for eth_accounts, in order to record the time the
// accounts were last seen or approved by the origin.
newEntries = result
.map((perm) => {
if (perm.parentCapability === 'eth_accounts') {
accounts = this.getAccountsFromPermission(perm)
}
@ -245,13 +239,10 @@ export default class PermissionsLogController {
return perm.parentCapability
})
.reduce((acc, method) => {
// all approved permissions will be included in the response,
// not just the newly requested ones
if (requestedMethods.includes(method)) {
if (method === 'eth_accounts') {
const accountToTimeMap = getAccountToTimeMap(accounts, time)
acc[method] = {
@ -281,7 +272,6 @@ export default class PermissionsLogController {
* @param {Object} newEntries - The new entries to commit.
*/
commitNewHistory(origin, newEntries) {
// a simple merge updates most permissions
const history = this.getHistory()
const newOriginHistory = {
@ -291,19 +281,16 @@ export default class PermissionsLogController {
// eth_accounts requires special handling, because of information
// we store about the accounts
const existingEthAccountsEntry = (
const existingEthAccountsEntry =
history[origin] && history[origin].eth_accounts
)
const newEthAccountsEntry = newEntries.eth_accounts
if (existingEthAccountsEntry && newEthAccountsEntry) {
// we may intend to update just the accounts, not the permission
// itself
const lastApproved = (
const lastApproved =
newEthAccountsEntry.lastApproved ||
existingEthAccountsEntry.lastApproved
)
// merge old and new eth_accounts history entries
newOriginHistory.eth_accounts = {
@ -346,19 +333,16 @@ export default class PermissionsLogController {
* @returns {Array<string>} The permitted accounts.
*/
getAccountsFromPermission(perm) {
if (perm.parentCapability !== 'eth_accounts' || !perm.caveats) {
return []
}
const accounts = new Set()
for (const caveat of perm.caveats) {
if (
caveat.name === CAVEAT_NAMES.exposedAccounts &&
Array.isArray(caveat.value)
) {
for (const value of caveat.value) {
accounts.add(value)
}
@ -378,7 +362,5 @@ export default class PermissionsLogController {
* @returns {Object} A string:number map of addresses to time.
*/
function getAccountToTimeMap(accounts, time) {
return accounts.reduce(
(acc, account) => ({ ...acc, [account]: time }), {},
)
return accounts.reduce((acc, account) => ({ ...acc, [account]: time }), {})
}

View File

@ -12,26 +12,21 @@ export default function createPermissionsMethodMiddleware ({
notifyAccountsChanged,
requestAccountsPermission,
}) {
let isProcessingRequestAccounts = false
return createAsyncMiddleware(async (req, res, next) => {
let responseHandler
switch (req.method) {
// Intercepting eth_accounts requests for backwards compatibility:
// The getAccounts call below wraps the rpc-cap middleware, and returns
// an empty array in case of errors (such as 4100:unauthorized)
case 'eth_accounts': {
res.result = await getAccounts()
return
}
case 'eth_requestAccounts': {
if (isProcessingRequestAccounts) {
res.error = ethErrors.rpc.resourceUnavailable(
'Already processing eth_requestAccounts. Please wait.',
@ -79,7 +74,6 @@ export default function createPermissionsMethodMiddleware ({
// custom method for getting metadata from the requesting domain,
// sent automatically by the inpage provider when it's initialized
case 'wallet_sendDomainMetadata': {
if (typeof req.domainMetadata?.name === 'string') {
addDomainMetadata(req.origin, req.domainMetadata)
}
@ -89,11 +83,8 @@ export default function createPermissionsMethodMiddleware ({
// register return handler to send accountsChanged notification
case 'wallet_requestPermissions': {
if ('eth_accounts' in req.params?.[0]) {
responseHandler = async () => {
if (Array.isArray(res.result)) {
for (const permission of res.result) {
if (permission.parentCapability === 'eth_accounts') {

View File

@ -1,6 +1,9 @@
export default function getRestrictedMethods ({ getIdentities, getKeyringAccounts }) {
export default function getRestrictedMethods({
getIdentities,
getKeyringAccounts,
}) {
return {
'eth_accounts': {
eth_accounts: {
method: async (_, res, __, end) => {
try {
const accounts = await getKeyringAccounts()
@ -10,7 +13,10 @@ export default function getRestrictedMethods ({ getIdentities, getKeyringAccount
throw new Error(`Missing identity for address ${firstAddress}`)
} else if (!identities[secondAddress]) {
throw new Error(`Missing identity for address ${secondAddress}`)
} else if (identities[firstAddress].lastSelected === identities[secondAddress].lastSelected) {
} else if (
identities[firstAddress].lastSelected ===
identities[secondAddress].lastSelected
) {
return 0
} else if (identities[firstAddress].lastSelected === undefined) {
return 1
@ -18,7 +24,10 @@ export default function getRestrictedMethods ({ getIdentities, getKeyringAccount
return -1
}
return identities[secondAddress].lastSelected - identities[firstAddress].lastSelected
return (
identities[secondAddress].lastSelected -
identities[firstAddress].lastSelected
)
})
end()
} catch (err) {

View File

@ -9,7 +9,6 @@ import { addInternalMethodPrefix } from './permissions'
import { NETWORK_TYPE_TO_ID_MAP } from './network/enums'
export default class PreferencesController {
/**
*
* @typedef {Object} PreferencesController
@ -66,7 +65,8 @@ export default class PreferencesController {
metaMetricsSendCount: 0,
// ENS decentralized website resolution
ipfsGateway: 'dweb.link', ...opts.initState,
ipfsGateway: 'dweb.link',
...opts.initState,
}
this.network = opts.network
@ -131,7 +131,12 @@ export default class PreferencesController {
this.store.updateState({ participateInMetaMetrics: bool })
let metaMetricsId = null
if (bool && !this.store.getState().metaMetricsId) {
metaMetricsId = bufferToHex(sha3(String(Date.now()) + String(Math.round(Math.random() * Number.MAX_SAFE_INTEGER))))
metaMetricsId = bufferToHex(
sha3(
String(Date.now()) +
String(Math.round(Math.random() * Number.MAX_SAFE_INTEGER)),
),
)
this.store.updateState({ metaMetricsId })
} else if (bool === false) {
this.store.updateState({ metaMetricsId })
@ -228,7 +233,9 @@ export default class PreferencesController {
*
*/
setCurrentLocale(key) {
const textDirection = (['ar', 'dv', 'fa', 'he', 'ku'].includes(key)) ? 'rtl' : 'auto'
const textDirection = ['ar', 'dv', 'fa', 'he', 'ku'].includes(key)
? 'rtl'
: 'auto'
this.store.updateState({
currentLocale: key,
textDirection,
@ -315,7 +322,6 @@ export default class PreferencesController {
* @returns {Promise<string>} - selectedAddress the selected address.
*/
syncAddresses(addresses) {
if (!Array.isArray(addresses) || addresses.length === 0) {
throw new Error('Expected non-empty array of addresses.')
}
@ -332,7 +338,6 @@ export default class PreferencesController {
// Identities are no longer present.
if (Object.keys(newlyLost).length > 0) {
// store lost accounts
Object.keys(newlyLost).forEach((key) => {
lostIdentities[key] = newlyLost[key]
@ -467,7 +472,9 @@ export default class PreferencesController {
*/
setAccountLabel(account, label) {
if (!account) {
throw new Error(`setAccountLabel requires a valid address, got ${String(account)}`)
throw new Error(
`setAccountLabel requires a valid address, got ${String(account)}`,
)
}
const address = normalizeAddress(account)
const { identities } = this.store.getState()
@ -497,7 +504,6 @@ export default class PreferencesController {
const rpcDetail = rpcList[index]
const updatedRpc = { ...rpcDetail, ...newRpcDetails }
if (rpcDetail.chainId !== updatedRpc.chainId) {
// When the chainId is changed, associated address book entries should
// also be migrated. The address book entries are keyed by the `network` state,
// which for custom networks is the chainId with a fallback to the networkId
@ -506,13 +512,17 @@ export default class PreferencesController {
let addressBookKey = rpcDetail.chainId
if (!addressBookKey) {
// We need to find the networkId to determine what these addresses were keyed by
const provider = new ethers.providers.JsonRpcProvider(rpcDetail.rpcUrl)
const provider = new ethers.providers.JsonRpcProvider(
rpcDetail.rpcUrl,
)
try {
addressBookKey = await provider.send('net_version')
assert(typeof addressBookKey === 'string')
} catch (error) {
log.debug(error)
log.warn(`Failed to get networkId from ${rpcDetail.rpcUrl}; skipping address book migration`)
log.warn(
`Failed to get networkId from ${rpcDetail.rpcUrl}; skipping address book migration`,
)
}
}
@ -521,10 +531,12 @@ export default class PreferencesController {
// on both networks, since we don't know which network each contact is intended for.
let duplicate = false
const builtInProviderNetworkIds = Object.values(NETWORK_TYPE_TO_ID_MAP)
.map((ids) => ids.networkId)
const otherRpcEntries = rpcList
.filter((entry) => entry.rpcUrl !== newRpcDetails.rpcUrl)
const builtInProviderNetworkIds = Object.values(
NETWORK_TYPE_TO_ID_MAP,
).map((ids) => ids.networkId)
const otherRpcEntries = rpcList.filter(
(entry) => entry.rpcUrl !== newRpcDetails.rpcUrl,
)
if (
builtInProviderNetworkIds.includes(addressBookKey) ||
otherRpcEntries.some((entry) => entry.chainId === addressBookKey)
@ -532,7 +544,11 @@ export default class PreferencesController {
duplicate = true
}
this.migrateAddressBookState(addressBookKey, updatedRpc.chainId, duplicate)
this.migrateAddressBookState(
addressBookKey,
updatedRpc.chainId,
duplicate,
)
}
rpcList[index] = updatedRpc
this.store.updateState({ frequentRpcListDetail: rpcList })
@ -552,7 +568,13 @@ export default class PreferencesController {
* @param {Object} [rpcPrefs] - Optional RPC preferences, such as the block explorer URL
*
*/
addToFrequentRpcList (rpcUrl, chainId, ticker = 'ETH', nickname = '', rpcPrefs = {}) {
addToFrequentRpcList(
rpcUrl,
chainId,
ticker = 'ETH',
nickname = '',
rpcPrefs = {},
) {
const rpcList = this.getFrequentRpcListDetail()
const index = rpcList.findIndex((element) => {
@ -695,7 +717,11 @@ export default class PreferencesController {
*
*/
_updateAccountTokens(tokens, assetImages) {
const { accountTokens, providerType, selectedAddress } = this._getTokenRelatedStates()
const {
accountTokens,
providerType,
selectedAddress,
} = this._getTokenRelatedStates()
accountTokens[selectedAddress][providerType] = tokens
this.store.updateState({ accountTokens, tokens, assetImages })
}
@ -752,7 +778,9 @@ export default class PreferencesController {
const tokenOpts = { rawAddress, decimals, symbol, image }
this.addSuggestedERC20Asset(tokenOpts)
return this.openPopup().then(() => {
const tokenAddresses = this.getTokens().filter((token) => token.address === normalizeAddress(rawAddress))
const tokenAddresses = this.getTokens().filter(
(token) => token.address === normalizeAddress(rawAddress),
)
return tokenAddresses.length > 0
})
}
@ -768,14 +796,18 @@ export default class PreferencesController {
_validateERC20AssetParams(opts) {
const { rawAddress, symbol, decimals } = opts
if (!rawAddress || !symbol || typeof decimals === 'undefined') {
throw new Error(`Cannot suggest token without address, symbol, and decimals`)
throw new Error(
`Cannot suggest token without address, symbol, and decimals`,
)
}
if (!(symbol.length < 7)) {
throw new Error(`Invalid symbol ${symbol} more than six characters`)
}
const numDecimals = parseInt(decimals, 10)
if (isNaN(numDecimals) || numDecimals > 36 || numDecimals < 0) {
throw new Error(`Invalid decimals ${decimals} must be at least 0, and not over 36`)
throw new Error(
`Invalid decimals ${decimals} must be at least 0, and not over 36`,
)
}
if (!isValidAddress(rawAddress)) {
throw new Error(`Invalid address ${rawAddress}`)

View File

@ -28,17 +28,14 @@ const MAX_GAS_LIMIT = 2500000
// 3 seems to be an appropriate balance of giving users the time they need when MetaMask is not left idle, and turning polling off when it is.
const POLL_COUNT_LIMIT = 3
function calculateGasEstimateWithRefund (maxGas = MAX_GAS_LIMIT, estimatedRefund = 0, estimatedGas = 0) {
const maxGasMinusRefund = new BigNumber(
maxGas,
10,
)
.minus(estimatedRefund, 10)
function calculateGasEstimateWithRefund(
maxGas = MAX_GAS_LIMIT,
estimatedRefund = 0,
estimatedGas = 0,
) {
const maxGasMinusRefund = new BigNumber(maxGas, 10).minus(estimatedRefund, 10)
const gasEstimateWithRefund = maxGasMinusRefund.lt(
estimatedGas,
16,
)
const gasEstimateWithRefund = maxGasMinusRefund.lt(estimatedGas, 16)
? maxGasMinusRefund.toString(16)
: estimatedGas
@ -111,7 +108,11 @@ export default class SwapsController {
pollForNewQuotes() {
this.pollingTimeout = setTimeout(() => {
const { swapsState } = this.store.getState()
this.fetchAndSetQuotes(swapsState.fetchParams, swapsState.fetchParams?.metaData, true)
this.fetchAndSetQuotes(
swapsState.fetchParams,
swapsState.fetchParams?.metaData,
true,
)
}, QUOTE_POLLING_INTERVAL)
}
@ -119,7 +120,11 @@ export default class SwapsController {
clearTimeout(this.pollingTimeout)
}
async fetchAndSetQuotes (fetchParams, fetchParamsMetaData = {}, isPolledRequest) {
async fetchAndSetQuotes(
fetchParams,
fetchParamsMetaData = {},
isPolledRequest,
) {
if (!fetchParams) {
return null
}
@ -150,7 +155,10 @@ export default class SwapsController {
const quotesLastFetched = Date.now()
let approvalRequired = false
if (fetchParams.sourceToken !== ETH_SWAPS_TOKEN_ADDRESS && Object.values(newQuotes).length) {
if (
fetchParams.sourceToken !== ETH_SWAPS_TOKEN_ADDRESS &&
Object.values(newQuotes).length
) {
const allowance = await this._getERC20Allowance(
fetchParams.sourceToken,
fetchParams.fromAddress,
@ -167,7 +175,9 @@ export default class SwapsController {
approvalNeeded: null,
}))
} else if (!isPolledRequest) {
const { gasLimit: approvalGas } = await this.timedoutGasReturn(Object.values(newQuotes)[0].approvalNeeded)
const { gasLimit: approvalGas } = await this.timedoutGasReturn(
Object.values(newQuotes)[0].approvalNeeded,
)
newQuotes = mapValues(newQuotes, (quote) => ({
...quote,
@ -190,7 +200,9 @@ export default class SwapsController {
if (Object.values(newQuotes).length === 0) {
this.setSwapsErrorKey(QUOTES_NOT_AVAILABLE_ERROR)
} else {
const topQuoteData = await this._findTopQuoteAndCalculateSavings(newQuotes)
const topQuoteData = await this._findTopQuoteAndCalculateSavings(
newQuotes,
)
if (topQuoteData.topAggId) {
topAggId = topQuoteData.topAggId
@ -260,7 +272,9 @@ export default class SwapsController {
async getAllQuotesWithGasEstimates(quotes) {
const quoteGasData = await Promise.all(
Object.values(quotes).map(async (quote) => {
const { gasLimit, simulationFails } = await this.timedoutGasReturn(quote.trade)
const { gasLimit, simulationFails } = await this.timedoutGasReturn(
quote.trade,
)
return [gasLimit, simulationFails, quote.aggregator]
}),
)
@ -268,7 +282,11 @@ export default class SwapsController {
const newQuotes = {}
quoteGasData.forEach(([gasLimit, simulationFails, aggId]) => {
if (gasLimit && !simulationFails) {
const gasEstimateWithRefund = calculateGasEstimateWithRefund(quotes[aggId].maxGas, quotes[aggId].estimatedRefund, gasLimit)
const gasEstimateWithRefund = calculateGasEstimateWithRefund(
quotes[aggId].maxGas,
quotes[aggId].estimatedRefund,
gasLimit,
)
newQuotes[aggId] = {
...quotes[aggId],
@ -332,14 +350,21 @@ export default class SwapsController {
} = await this.timedoutGasReturn(quoteToUpdate.trade)
if (newGasEstimate && !simulationFails) {
const gasEstimateWithRefund = calculateGasEstimateWithRefund(quoteToUpdate.maxGas, quoteToUpdate.estimatedRefund, newGasEstimate)
const gasEstimateWithRefund = calculateGasEstimateWithRefund(
quoteToUpdate.maxGas,
quoteToUpdate.estimatedRefund,
newGasEstimate,
)
quoteToUpdate.gasEstimate = newGasEstimate
quoteToUpdate.gasEstimateWithRefund = gasEstimateWithRefund
}
this.store.updateState({
swapsState: { ...swapsState, quotes: { ...swapsState.quotes, [initialAggId]: quoteToUpdate } },
swapsState: {
...swapsState,
quotes: { ...swapsState.quotes, [initialAggId]: quoteToUpdate },
},
})
}
@ -386,7 +411,9 @@ export default class SwapsController {
setSwapsLiveness(swapsFeatureIsLive) {
const { swapsState } = this.store.getState()
this.store.updateState({ swapsState: { ...swapsState, swapsFeatureIsLive } })
this.store.updateState({
swapsState: { ...swapsState, swapsFeatureIsLive },
})
}
resetPostFetchState() {
@ -407,7 +434,11 @@ export default class SwapsController {
const { swapsState } = this.store.getState()
this.store.updateState({
swapsState: { ...initialState.swapsState, tokens: swapsState.tokens, swapsFeatureIsLive: swapsState.swapsFeatureIsLive },
swapsState: {
...initialState.swapsState,
tokens: swapsState.tokens,
swapsFeatureIsLive: swapsState.swapsFeatureIsLive,
},
})
clearTimeout(this.pollingTimeout)
}
@ -429,7 +460,7 @@ export default class SwapsController {
return {}
}
const usedGasPrice = customGasPrice || await this._getEthersGasPrice()
const usedGasPrice = customGasPrice || (await this._getEthersGasPrice())
let topAggId = ''
let ethTradeValueOfBestQuote = null
@ -468,8 +499,10 @@ export default class SwapsController {
// It always includes any external fees charged by the quote source. In
// addition, if the source asset is ETH, trade.value includes the amount
// of swapped ETH.
const totalWeiCost = new BigNumber(gasTotalInWeiHex, 16)
.plus(trade.value, 16)
const totalWeiCost = new BigNumber(gasTotalInWeiHex, 16).plus(
trade.value,
16,
)
const totalEthCost = conversionUtil(totalWeiCost, {
fromCurrency: 'ETH',
@ -482,7 +515,8 @@ export default class SwapsController {
// The total fee is aggregator/exchange fees plus gas fees.
// If the swap is from ETH, subtract the sourceAmount from the total cost.
// Otherwise, the total fee is simply trade.value plus gas fees.
const ethFee = sourceToken === ETH_SWAPS_TOKEN_ADDRESS
const ethFee =
sourceToken === ETH_SWAPS_TOKEN_ADDRESS
? conversionUtil(
totalWeiCost.minus(sourceAmount, 10), // sourceAmount is in wei
{
@ -540,10 +574,7 @@ export default class SwapsController {
// Performance savings are calculated as:
// medianFeeOfAllTrades - feeForBestTrade
savings.fee = getMedian(allEthFees).minus(
ethFeeForBestQuote,
10,
)
savings.fee = getMedian(allEthFees).minus(ethFeeForBestQuote, 10)
// Total savings are the sum of performance and fee savings
savings.total = savings.performance.plus(savings.fee, 10).toString(10)
@ -556,7 +587,9 @@ export default class SwapsController {
async _getERC20Allowance(contractAddress, walletAddress) {
const contract = new ethers.Contract(
contractAddress, abi, this.ethersProvider,
contractAddress,
abi,
this.ethersProvider,
)
return await contract.allowance(walletAddress, METASWAP_ADDRESS)
}
@ -577,7 +610,10 @@ export default class SwapsController {
if (window.navigator.onLine && intervalId === null) {
// Set the interval first to prevent race condition between listener and
// initial call to this function.
intervalId = setInterval(this._fetchAndSetSwapsLiveness.bind(this), TEN_MINUTES_MS)
intervalId = setInterval(
this._fetchAndSetSwapsLiveness.bind(this),
TEN_MINUTES_MS,
)
this._fetchAndSetSwapsLiveness()
}
}
@ -637,7 +673,9 @@ export default class SwapsController {
}
if (!successfullyFetched) {
log.error('Failed to fetch swaps feature flag 3 times. Setting to false and trying again next interval.')
log.error(
'Failed to fetch swaps feature flag 3 times. Setting to false and trying again next interval.',
)
}
if (swapsFeatureIsLive !== oldSwapsFeatureIsLive) {
@ -672,9 +710,7 @@ function getMedian (values) {
// return mean of middle two values
const upperIndex = values.length / 2
return values[upperIndex]
.plus(values[upperIndex - 1])
.dividedBy(2)
return values[upperIndex].plus(values[upperIndex - 1]).dividedBy(2)
}
export const utils = {

View File

@ -41,16 +41,22 @@ export default class ThreeBoxController {
const accounts = await this.keyringController.getAccounts()
if (isUnlocked && accounts[0]) {
const appKeyAddress = await this.keyringController.getAppKeyAddress(accounts[0], 'wallet://3box.metamask.io')
const appKeyAddress = await this.keyringController.getAppKeyAddress(
accounts[0],
'wallet://3box.metamask.io',
)
return [appKeyAddress]
}
return []
},
processPersonalMessage: async (msgParams) => {
const accounts = await this.keyringController.getAccounts()
return keyringController.signPersonalMessage({ ...msgParams, from: accounts[0] }, {
return keyringController.signPersonalMessage(
{ ...msgParams, from: accounts[0] },
{
withAppKeyOrigin: 'wallet://3box.metamask.io',
})
},
)
},
})
@ -65,7 +71,9 @@ export default class ThreeBoxController {
}
this.store = new ObservableStore(initState)
this.registeringUpdates = false
this.lastMigration = migrations.sort((a, b) => a.version - b.version).slice(-1)[0]
this.lastMigration = migrations
.sort((a, b) => a.version - b.version)
.slice(-1)[0]
if (initState.threeBoxSyncingAllowed) {
this.init()
@ -119,7 +127,10 @@ export default class ThreeBoxController {
async new3Box() {
const accounts = await this.keyringController.getAccounts()
this.address = await this.keyringController.getAppKeyAddress(accounts[0], 'wallet://3box.metamask.io')
this.address = await this.keyringController.getAppKeyAddress(
accounts[0],
'wallet://3box.metamask.io',
)
let backupExists
try {
const threeBoxConfig = await Box.getConfig(this.address)
@ -183,7 +194,9 @@ export default class ThreeBoxController {
PreferencesController: preferences,
AddressBookController: addressBook,
}
const initialMigrationState = migrator.generateInitialState(formattedStateBackup)
const initialMigrationState = migrator.generateInitialState(
formattedStateBackup,
)
const migratedState = await migrator.migrateData(initialMigrationState)
return {
preferences: migratedState.data.PreferencesController,
@ -193,10 +206,9 @@ export default class ThreeBoxController {
async restoreFromThreeBox() {
const backedUpState = await this.space.private.get('metamaskBackup')
const {
preferences,
addressBook,
} = await this.migrateBackedUpState(backedUpState)
const { preferences, addressBook } = await this.migrateBackedUpState(
backedUpState,
)
this.store.updateState({ threeBoxLastUpdated: backedUpState.lastUpdated })
preferences && this.preferencesController.store.updateState(preferences)
addressBook && this.addressBookController.update(addressBook, true)

View File

@ -11,7 +11,6 @@ const DEFAULT_INTERVAL = 180 * 1000
* rates based on a user's current token list
*/
export default class TokenRatesController {
/**
* Creates a TokenRatesController
*
@ -28,19 +27,30 @@ export default class TokenRatesController {
*/
async updateExchangeRates() {
const contractExchangeRates = {}
const nativeCurrency = this.currency ? this.currency.state.nativeCurrency.toLowerCase() : 'eth'
const nativeCurrency = this.currency
? this.currency.state.nativeCurrency.toLowerCase()
: 'eth'
const pairs = this._tokens.map((token) => token.address).join(',')
const query = `contract_addresses=${pairs}&vs_currencies=${nativeCurrency}`
if (this._tokens.length > 0) {
try {
const response = await window.fetch(`https://api.coingecko.com/api/v3/simple/token_price/ethereum?${query}`)
const response = await window.fetch(
`https://api.coingecko.com/api/v3/simple/token_price/ethereum?${query}`,
)
const prices = await response.json()
this._tokens.forEach((token) => {
const price = prices[token.address.toLowerCase()] || prices[ethUtil.toChecksumAddress(token.address)]
contractExchangeRates[normalizeAddress(token.address)] = price ? price[nativeCurrency] : 0
const price =
prices[token.address.toLowerCase()] ||
prices[ethUtil.toChecksumAddress(token.address)]
contractExchangeRates[normalizeAddress(token.address)] = price
? price[nativeCurrency]
: 0
})
} catch (error) {
log.warn(`MetaMask - TokenRatesController exchange rate fetch failed.`, error)
log.warn(
`MetaMask - TokenRatesController exchange rate fetch failed.`,
error,
)
}
}
this.store.putState({ contractExchangeRates })

View File

@ -95,8 +95,12 @@ export default class TransactionController extends EventEmitter {
this.nonceTracker = new NonceTracker({
provider: this.provider,
blockTracker: this.blockTracker,
getPendingTransactions: this.txStateManager.getPendingTransactions.bind(this.txStateManager),
getConfirmedTransactions: this.txStateManager.getConfirmedTransactions.bind(this.txStateManager),
getPendingTransactions: this.txStateManager.getPendingTransactions.bind(
this.txStateManager,
),
getConfirmedTransactions: this.txStateManager.getConfirmedTransactions.bind(
this.txStateManager,
),
})
this.pendingTxTracker = new PendingTransactionTracker({
@ -109,7 +113,9 @@ export default class TransactionController extends EventEmitter {
return [...pending, ...approved]
},
approveTransaction: this.approveTransaction.bind(this),
getCompletedTransactions: this.txStateManager.getConfirmedTransactions.bind(this.txStateManager),
getCompletedTransactions: this.txStateManager.getConfirmedTransactions.bind(
this.txStateManager,
),
})
this.txStateManager.store.subscribe(() => this.emit('update:badge'))
@ -167,25 +173,50 @@ export default class TransactionController extends EventEmitter {
* @param {Object} opts - with the key origin to put the origin on the txMeta
*/
async newUnapprovedTransaction(txParams, opts = {}) {
log.debug(
`MetaMaskController newUnapprovedTransaction ${JSON.stringify(txParams)}`,
)
log.debug(`MetaMaskController newUnapprovedTransaction ${JSON.stringify(txParams)}`)
const initialTxMeta = await this.addUnapprovedTransaction(txParams, opts.origin)
const initialTxMeta = await this.addUnapprovedTransaction(
txParams,
opts.origin,
)
// listen for tx completion (success, fail)
return new Promise((resolve, reject) => {
this.txStateManager.once(`${initialTxMeta.id}:finished`, (finishedTxMeta) => {
this.txStateManager.once(
`${initialTxMeta.id}:finished`,
(finishedTxMeta) => {
switch (finishedTxMeta.status) {
case 'submitted':
return resolve(finishedTxMeta.hash)
case 'rejected':
return reject(cleanErrorStack(ethErrors.provider.userRejectedRequest('MetaMask Tx Signature: User denied transaction signature.')))
return reject(
cleanErrorStack(
ethErrors.provider.userRejectedRequest(
'MetaMask Tx Signature: User denied transaction signature.',
),
),
)
case 'failed':
return reject(cleanErrorStack(ethErrors.rpc.internal(finishedTxMeta.err.message)))
return reject(
cleanErrorStack(
ethErrors.rpc.internal(finishedTxMeta.err.message),
),
)
default:
return reject(cleanErrorStack(ethErrors.rpc.internal(`MetaMask Tx Signature: Unknown problem: ${JSON.stringify(finishedTxMeta.txParams)}`)))
return reject(
cleanErrorStack(
ethErrors.rpc.internal(
`MetaMask Tx Signature: Unknown problem: ${JSON.stringify(
finishedTxMeta.txParams,
)}`,
),
),
)
}
})
},
)
})
}
@ -196,7 +227,6 @@ export default class TransactionController extends EventEmitter {
* @returns {txMeta}
*/
async addUnapprovedTransaction(txParams, origin) {
// validate
const normalizedTxParams = txUtils.normalizeTxParams(txParams)
@ -236,7 +266,10 @@ export default class TransactionController extends EventEmitter {
txMeta.origin = origin
const { transactionCategory, getCodeResponse } = await this._determineTransactionCategory(txParams)
const {
transactionCategory,
getCodeResponse,
} = await this._determineTransactionCategory(txParams)
txMeta.transactionCategory = transactionCategory
// ensure value
@ -271,7 +304,10 @@ export default class TransactionController extends EventEmitter {
*/
async addTxGasDefaults(txMeta, getCodeResponse) {
const defaultGasPrice = await this._getDefaultGasPrice(txMeta)
const { gasLimit: defaultGasLimit, simulationFails } = await this._getDefaultGasLimit(txMeta, getCodeResponse)
const {
gasLimit: defaultGasLimit,
simulationFails,
} = await this._getDefaultGasLimit(txMeta, getCodeResponse)
// eslint-disable-next-line no-param-reassign
txMeta = this.txStateManager.getTx(txMeta.id)
@ -316,7 +352,9 @@ export default class TransactionController extends EventEmitter {
) {
// if there's data in the params, but there's no contract code, it's not a valid transaction
if (txMeta.txParams.data) {
const err = new Error('TxGasUtil - Trying to call a function on a non-contract address')
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
@ -329,10 +367,17 @@ export default class TransactionController extends EventEmitter {
return { gasLimit: SIMPLE_GAS_COST }
}
const { blockGasLimit, estimatedGasHex, simulationFails } = await this.txGasUtil.analyzeGasUsage(txMeta)
const {
blockGasLimit,
estimatedGasHex,
simulationFails,
} = await this.txGasUtil.analyzeGasUsage(txMeta)
// add additional gas buffer to our estimation for safety
const gasLimit = this.txGasUtil.addGasBuffer(ethUtil.addHexPrefix(estimatedGasHex), blockGasLimit)
const gasLimit = this.txGasUtil.addGasBuffer(
ethUtil.addHexPrefix(estimatedGasHex),
blockGasLimit,
)
return { gasLimit, simulationFails }
}
@ -349,7 +394,9 @@ export default class TransactionController extends EventEmitter {
const { txParams } = originalTxMeta
const { gasPrice: lastGasPrice, from, nonce } = txParams
const newGasPrice = customGasPrice || bnToHex(BnMultiplyByFraction(hexToBn(lastGasPrice), 11, 10))
const newGasPrice =
customGasPrice ||
bnToHex(BnMultiplyByFraction(hexToBn(lastGasPrice), 11, 10))
const newTxMeta = this.txStateManager.generateTxMeta({
txParams: {
from,
@ -385,7 +432,9 @@ export default class TransactionController extends EventEmitter {
const { txParams } = originalTxMeta
const { gasPrice: lastGasPrice } = txParams
const newGasPrice = customGasPrice || bnToHex(BnMultiplyByFraction(hexToBn(lastGasPrice), 11, 10))
const newGasPrice =
customGasPrice ||
bnToHex(BnMultiplyByFraction(hexToBn(lastGasPrice), 11, 10))
const newTxMeta = this.txStateManager.generateTxMeta({
txParams: {
@ -456,8 +505,11 @@ export default class TransactionController extends EventEmitter {
// add nonce to txParams
// if txMeta has lastGasPrice then it is a retry at same nonce with higher
// gas price transaction and their for the nonce should not be calculated
const nonce = txMeta.lastGasPrice ? txMeta.txParams.nonce : nonceLock.nextNonce
const customOrNonce = (customNonceValue === 0) ? customNonceValue : customNonceValue || nonce
const nonce = txMeta.lastGasPrice
? txMeta.txParams.nonce
: nonceLock.nextNonce
const customOrNonce =
customNonceValue === 0 ? customNonceValue : customNonceValue || nonce
txMeta.txParams.nonce = ethUtil.addHexPrefix(customOrNonce.toString(16))
// add nonce debugging information to txMeta
@ -510,7 +562,10 @@ export default class TransactionController extends EventEmitter {
txMeta.s = ethUtil.bufferToHex(ethTx.s)
txMeta.v = ethUtil.bufferToHex(ethTx.v)
this.txStateManager.updateTx(txMeta, 'transactions#signTransaction: add r, s, v values')
this.txStateManager.updateTx(
txMeta,
'transactions#signTransaction: add r, s, v values',
)
// set state to signed
this.txStateManager.setTxStatusSigned(txMeta.id)
@ -566,7 +621,8 @@ export default class TransactionController extends EventEmitter {
try {
// 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'
const gasUsed =
typeof txReceipt.gasUsed === 'string'
? txReceipt.gasUsed
: txReceipt.gasUsed.toString(16)
@ -577,7 +633,10 @@ export default class TransactionController extends EventEmitter {
this.txStateManager.setTxStatusConfirmed(txId)
this._markNonceDuplicatesDropped(txId)
this.txStateManager.updateTx(txMeta, 'transactions#confirmTransaction - add txReceipt')
this.txStateManager.updateTx(
txMeta,
'transactions#confirmTransaction - add txReceipt',
)
if (txMeta.transactionCategory === SWAP) {
const postTxBalance = await this.query.getBalance(txMeta.txParams.from)
@ -589,11 +648,13 @@ export default class TransactionController extends EventEmitter {
latestTxMeta.postTxBalance = postTxBalance.toString(16)
this.txStateManager.updateTx(latestTxMeta, 'transactions#confirmTransaction - add postTxBalance')
this.txStateManager.updateTx(
latestTxMeta,
'transactions#confirmTransaction - add postTxBalance',
)
this._trackSwapsMetrics(latestTxMeta, approvalTxMeta)
}
} catch (err) {
log.error(err)
}
@ -625,7 +686,6 @@ export default class TransactionController extends EventEmitter {
//
/** maps methods for convenience*/
_mapMethods() {
/** @returns {Object} - the state in transaction controller */
this.getState = () => this.memStore.getState()
@ -633,19 +693,23 @@ export default class TransactionController extends EventEmitter {
this.getNetwork = () => this.networkStore.getState()
/** @returns {string} - the user selected address */
this.getSelectedAddress = () => this.preferencesStore.getState().selectedAddress
this.getSelectedAddress = () =>
this.preferencesStore.getState().selectedAddress
/** @returns {array} - transactions whos status is unapproved */
this.getUnapprovedTxCount = () => Object.keys(this.txStateManager.getUnapprovedTxList()).length
this.getUnapprovedTxCount = () =>
Object.keys(this.txStateManager.getUnapprovedTxList()).length
/**
@returns {number} - number of transactions that have the status submitted
@param {string} account - hex prefixed account
*/
this.getPendingTxCount = (account) => this.txStateManager.getPendingTransactions(account).length
this.getPendingTxCount = (account) =>
this.txStateManager.getPendingTransactions(account).length
/** see txStateManager */
this.getFilteredTxList = (opts) => this.txStateManager.getFilteredTxList(opts)
this.getFilteredTxList = (opts) =>
this.txStateManager.getFilteredTxList(opts)
}
// called once on startup
@ -663,27 +727,39 @@ export default class TransactionController extends EventEmitter {
*/
_onBootCleanUp() {
this.txStateManager.getFilteredTxList({
this.txStateManager
.getFilteredTxList({
status: 'unapproved',
loadingDefaults: true,
}).forEach((tx) => {
})
.forEach((tx) => {
this.addTxGasDefaults(tx)
.then((txMeta) => {
txMeta.loadingDefaults = false
this.txStateManager.updateTx(txMeta, 'transactions: gas estimation for tx on boot')
}).catch((error) => {
this.txStateManager.updateTx(
txMeta,
'transactions: gas estimation for tx on boot',
)
})
.catch((error) => {
const txMeta = this.txStateManager.getTx(tx.id)
txMeta.loadingDefaults = false
this.txStateManager.updateTx(txMeta, 'failed to estimate gas during boot cleanup.')
this.txStateManager.updateTx(
txMeta,
'failed to estimate gas during boot cleanup.',
)
this.txStateManager.setTxStatusFailed(txMeta.id, error)
})
})
this.txStateManager.getFilteredTxList({
this.txStateManager
.getFilteredTxList({
status: TRANSACTION_STATUS_APPROVED,
}).forEach((txMeta) => {
const txSignError = new Error('Transaction found as "approved" during boot - possibly stuck during signing')
})
.forEach((txMeta) => {
const txSignError = new Error(
'Transaction found as "approved" during boot - possibly stuck during signing',
)
this.txStateManager.setTxStatusFailed(txMeta.id, txSignError)
})
}
@ -693,18 +769,35 @@ export default class TransactionController extends EventEmitter {
and blockTracker
*/
_setupListeners() {
this.txStateManager.on('tx:status-update', this.emit.bind(this, 'tx:status-update'))
this.txStateManager.on(
'tx:status-update',
this.emit.bind(this, 'tx:status-update'),
)
this._setupBlockTrackerListener()
this.pendingTxTracker.on('tx:warning', (txMeta) => {
this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:warning')
this.txStateManager.updateTx(
txMeta,
'transactions/pending-tx-tracker#event: tx:warning',
)
})
this.pendingTxTracker.on('tx:failed', this.txStateManager.setTxStatusFailed.bind(this.txStateManager))
this.pendingTxTracker.on('tx:confirmed', (txId, transactionReceipt) => this.confirmTransaction(txId, transactionReceipt))
this.pendingTxTracker.on('tx:dropped', this.txStateManager.setTxStatusDropped.bind(this.txStateManager))
this.pendingTxTracker.on(
'tx:failed',
this.txStateManager.setTxStatusFailed.bind(this.txStateManager),
)
this.pendingTxTracker.on('tx:confirmed', (txId, transactionReceipt) =>
this.confirmTransaction(txId, transactionReceipt),
)
this.pendingTxTracker.on(
'tx:dropped',
this.txStateManager.setTxStatusDropped.bind(this.txStateManager),
)
this.pendingTxTracker.on('tx:block-update', (txMeta, latestBlockNumber) => {
if (!txMeta.firstRetryBlockNumber) {
txMeta.firstRetryBlockNumber = latestBlockNumber
this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:block-update')
this.txStateManager.updateTx(
txMeta,
'transactions/pending-tx-tracker#event: tx:block-update',
)
}
})
this.pendingTxTracker.on('tx:retry', (txMeta) => {
@ -712,7 +805,10 @@ export default class TransactionController extends EventEmitter {
txMeta.retryCount = 0
}
txMeta.retryCount += 1
this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:retry')
this.txStateManager.updateTx(
txMeta,
'transactions/pending-tx-tracker#event: tx:retry',
)
})
}
@ -779,7 +875,10 @@ export default class TransactionController extends EventEmitter {
return
}
otherTxMeta.replacedBy = txMeta.hash
this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:confirmed reference to confirmed txHash with same nonce')
this.txStateManager.updateTx(
txMeta,
'transactions/pending-tx-tracker#event: tx:confirmed reference to confirmed txHash with same nonce',
)
this.txStateManager.setTxStatusDropped(otherTxMeta.id)
})
}
@ -822,7 +921,9 @@ export default class TransactionController extends EventEmitter {
*/
_updateMemstore() {
const unapprovedTxs = this.txStateManager.getUnapprovedTxList()
const currentNetworkTxList = this.txStateManager.getTxList(MAX_MEMSTORE_TX_LIST_SIZE)
const currentNetworkTxList = this.txStateManager.getTxList(
MAX_MEMSTORE_TX_LIST_SIZE,
)
this.memStore.updateState({ unapprovedTxs, currentNetworkTxList })
}
@ -851,19 +952,18 @@ export default class TransactionController extends EventEmitter {
approvalTxMeta,
)
const quoteVsExecutionRatio = `${
(new BigNumber(tokensReceived, 10))
const quoteVsExecutionRatio = `${new BigNumber(tokensReceived, 10)
.div(txMeta.swapMetaData.token_to_amount, 10)
.times(100)
.round(2)
}%`
.round(2)}%`
const estimatedVsUsedGasRatio = `${
(new BigNumber(txMeta.txReceipt.gasUsed, 16))
const estimatedVsUsedGasRatio = `${new BigNumber(
txMeta.txReceipt.gasUsed,
16,
)
.div(txMeta.swapMetaData.estimated_gas, 10)
.times(100)
.round(2)
}%`
.round(2)}%`
this._trackMetaMetricsEvent({
event: 'Swap Completed',

View File

@ -50,7 +50,9 @@ export function generateHistoryEntry (previousState, newState, note) {
*/
export function replayHistory(_shortHistory) {
const shortHistory = cloneDeep(_shortHistory)
return shortHistory.reduce((val, entry) => jsonDiffer.applyPatch(val, entry).newDocument)
return shortHistory.reduce(
(val, entry) => jsonDiffer.applyPatch(val, entry).newDocument,
)
}
/**

View File

@ -2,7 +2,8 @@ import { addHexPrefix, isValidAddress } from 'ethereumjs-util'
const normalizers = {
from: (from) => addHexPrefix(from),
to: (to, lowerCase) => (lowerCase ? addHexPrefix(to).toLowerCase() : addHexPrefix(to)),
to: (to, lowerCase) =>
lowerCase ? addHexPrefix(to).toLowerCase() : addHexPrefix(to),
nonce: (nonce) => addHexPrefix(nonce),
value: (value) => addHexPrefix(value),
data: (data) => addHexPrefix(data),
@ -39,11 +40,15 @@ export function validateTxParams (txParams) {
if ('value' in txParams) {
const value = txParams.value.toString()
if (value.includes('-')) {
throw new Error(`Invalid transaction value of ${txParams.value} not a positive number.`)
throw new Error(
`Invalid transaction value of ${txParams.value} not a positive number.`,
)
}
if (value.includes('.')) {
throw new Error(`Invalid transaction value of ${txParams.value} number must be in wei`)
throw new Error(
`Invalid transaction value of ${txParams.value} number must be in wei`,
)
}
}
}

View File

@ -19,7 +19,6 @@ import EthQuery from 'ethjs-query'
*/
export default class PendingTransactionTracker extends EventEmitter {
/**
* We wait this many blocks before emitting a 'tx:dropped' event
*
@ -39,7 +38,7 @@ export default class PendingTransactionTracker extends EventEmitter {
constructor(config) {
super()
this.query = config.query || (new EthQuery(config.provider))
this.query = config.query || new EthQuery(config.provider)
this.nonceTracker = config.nonceTracker
this.getPendingTransactions = config.getPendingTransactions
this.getCompletedTransactions = config.getCompletedTransactions
@ -56,9 +55,13 @@ export default class PendingTransactionTracker extends EventEmitter {
const nonceGlobalLock = await this.nonceTracker.getGlobalLock()
try {
const pendingTxs = this.getPendingTransactions()
await Promise.all(pendingTxs.map((txMeta) => this._checkPendingTx(txMeta)))
await Promise.all(
pendingTxs.map((txMeta) => this._checkPendingTx(txMeta)),
)
} catch (err) {
log.error('PendingTransactionTracker - Error updating pending transactions')
log.error(
'PendingTransactionTracker - Error updating pending transactions',
)
log.error(err)
}
nonceGlobalLock.releaseLock()
@ -79,18 +82,20 @@ export default class PendingTransactionTracker extends EventEmitter {
try {
await this._resubmitTx(txMeta, blockNumber)
} catch (err) {
const errorMessage = err.value?.message?.toLowerCase() || err.message.toLowerCase()
const isKnownTx = (
const errorMessage =
err.value?.message?.toLowerCase() || err.message.toLowerCase()
const isKnownTx =
// geth
errorMessage.includes('replacement transaction underpriced') ||
errorMessage.includes('known transaction') ||
// parity
errorMessage.includes('gas price too low to replace') ||
errorMessage.includes('transaction with the same hash was already imported') ||
errorMessage.includes(
'transaction with the same hash was already imported',
) ||
// other
errorMessage.includes('gateway timeout') ||
errorMessage.includes('nonce too low')
)
// ignore resubmit warnings, return early
if (isKnownTx) {
return
@ -122,8 +127,11 @@ export default class PendingTransactionTracker extends EventEmitter {
this.emit('tx:block-update', txMeta, latestBlockNumber)
}
const firstRetryBlockNumber = txMeta.firstRetryBlockNumber || latestBlockNumber
const txBlockDistance = Number.parseInt(latestBlockNumber, 16) - Number.parseInt(firstRetryBlockNumber, 16)
const firstRetryBlockNumber =
txMeta.firstRetryBlockNumber || latestBlockNumber
const txBlockDistance =
Number.parseInt(latestBlockNumber, 16) -
Number.parseInt(firstRetryBlockNumber, 16)
const retryCount = txMeta.retryCount || 0
@ -167,7 +175,9 @@ export default class PendingTransactionTracker extends EventEmitter {
// extra check in case there was an uncaught error during the
// signature and submission process
if (!txHash) {
const noTxHashErr = new Error('We had an error while submitting this transaction, please try again.')
const noTxHashErr = new Error(
'We had an error while submitting this transaction, please try again.',
)
noTxHashErr.name = 'NoTxHashError'
this.emit('tx:failed', txId, noTxHashErr)
@ -207,7 +217,10 @@ export default class PendingTransactionTracker extends EventEmitter {
* @private
*/
async _checkIfTxWasDropped(txMeta) {
const { hash: txHash, txParams: { nonce, from } } = txMeta
const {
hash: txHash,
txParams: { nonce, from },
} = txMeta
const networkNextNonce = await this.query.getTransactionCount(from)
if (parseInt(nonce, 16) >= networkNextNonce.toNumber()) {
@ -242,7 +255,9 @@ export default class PendingTransactionTracker extends EventEmitter {
// This is called while the transaction is in-flight, so it is possible that the
// list of completed transactions now includes the transaction we were looking at
// and if that is the case, don't consider the transaction to have taken its own nonce
(other) => !(other.id === txMeta.id) && other.txParams.nonce === txMeta.txParams.nonce,
(other) =>
!(other.id === txMeta.id) &&
other.txParams.nonce === txMeta.txParams.nonce,
)
}
}

View File

@ -20,7 +20,6 @@ and used to do things like calculate gas of a tx.
*/
export default class TxGasUtil {
constructor(provider) {
this.query = new EthQuery(provider)
}
@ -89,10 +88,18 @@ export default class TxGasUtil {
}
async getBufferedGasLimit(txMeta, multiplier) {
const { blockGasLimit, estimatedGasHex, simulationFails } = await this.analyzeGasUsage(txMeta)
const {
blockGasLimit,
estimatedGasHex,
simulationFails,
} = await this.analyzeGasUsage(txMeta)
// add additional gas buffer to our estimation for safety
const gasLimit = this.addGasBuffer(ethUtil.addHexPrefix(estimatedGasHex), blockGasLimit, multiplier)
const gasLimit = this.addGasBuffer(
ethUtil.addHexPrefix(estimatedGasHex),
blockGasLimit,
multiplier,
)
return { gasLimit, simulationFails }
}
}

View File

@ -2,7 +2,11 @@ import EventEmitter from 'safe-event-emitter'
import ObservableStore from 'obs-store'
import log from 'loglevel'
import createId from '../../lib/random-id'
import { generateHistoryEntry, replayHistory, snapshotFromTxMeta } from './lib/tx-state-history-helpers'
import {
generateHistoryEntry,
replayHistory,
snapshotFromTxMeta,
} from './lib/tx-state-history-helpers'
import { getFinalStates, normalizeTxParams } from './lib/util'
/**
@ -31,9 +35,7 @@ export default class TransactionStateManager extends EventEmitter {
constructor({ initState, txHistoryLimit, getNetwork }) {
super()
this.store = new ObservableStore(
{ transactions: [], ...initState },
)
this.store = new ObservableStore({ transactions: [], ...initState })
this.txHistoryLimit = txHistoryLimit
this.getNetwork = getNetwork
}
@ -49,10 +51,11 @@ export default class TransactionStateManager extends EventEmitter {
}
return {
id: createId(),
time: (new Date()).getTime(),
time: new Date().getTime(),
status: 'unapproved',
metamaskNetworkId: netId,
loadingDefaults: true, ...opts,
loadingDefaults: true,
...opts,
}
}
@ -193,8 +196,9 @@ export default class TransactionStateManager extends EventEmitter {
transactions.splice(index, 1)
}
}
const newTxIndex = transactions
.findIndex((currentTxMeta) => currentTxMeta.time > txMeta.time)
const newTxIndex = transactions.findIndex(
(currentTxMeta) => currentTxMeta.time > txMeta.time,
)
newTxIndex === -1
? transactions.push(txMeta)
@ -279,12 +283,16 @@ export default class TransactionStateManager extends EventEmitter {
switch (key) {
case 'chainId':
if (typeof value !== 'number' && typeof value !== 'string') {
throw new Error(`${key} in txParams is not a Number or hex string. got: (${value})`)
throw new Error(
`${key} in txParams is not a Number or hex string. got: (${value})`,
)
}
break
default:
if (typeof value !== 'string') {
throw new Error(`${key} in txParams is not a string. got: (${value})`)
throw new Error(
`${key} in txParams is not a string. got: (${value})`,
)
}
break
}
@ -397,7 +405,7 @@ export default class TransactionStateManager extends EventEmitter {
*/
setTxStatusSubmitted(txId) {
const txMeta = this.getTx(txId)
txMeta.submittedTime = (new Date()).getTime()
txMeta.submittedTime = new Date().getTime()
this.updateTx(txMeta, 'txStateManager - add submitted time stamp')
this._setTxStatus(txId, 'submitted')
}
@ -448,7 +456,13 @@ export default class TransactionStateManager extends EventEmitter {
const network = this.getNetwork()
// Filter out the ones from the current account and network
const otherAccountTxs = txs.filter((txMeta) => !(txMeta.txParams.from === address && txMeta.metamaskNetworkId === network))
const otherAccountTxs = txs.filter(
(txMeta) =>
!(
txMeta.txParams.from === address &&
txMeta.metamaskNetworkId === network
),
)
// Update state
this._saveTxList(otherAccountTxs)
@ -516,7 +530,9 @@ export default class TransactionStateManager extends EventEmitter {
clearUnapprovedTxs() {
const transactions = this.getFullTxList()
const nonUnapprovedTxs = transactions.filter((tx) => tx.status !== 'unapproved')
const nonUnapprovedTxs = transactions.filter(
(tx) => tx.status !== 'unapproved',
)
this._saveTxList(nonUnapprovedTxs)
}
}

View File

@ -1,4 +1,3 @@
/**
* @typedef {Object} FirstTimeState
* @property {Object} config Initial configuration parameters

View File

@ -5,7 +5,6 @@ import ObservableStore from 'obs-store'
* structure of child stores based on configuration
*/
export default class ComposableObservableStore extends ObservableStore {
/**
* Create a new store
*
@ -45,7 +44,9 @@ export default class ComposableObservableStore extends ObservableStore {
for (const key in this.config) {
if (Object.prototype.hasOwnProperty.call(this.config, key)) {
const controller = this.config[key]
const state = controller.getState ? controller.getState() : controller.state
const state = controller.getState
? controller.getState()
: controller.state
flatState = { ...flatState, ...state }
}
}

View File

@ -14,7 +14,12 @@ import log from 'loglevel'
import pify from 'pify'
import Web3 from 'web3'
import SINGLE_CALL_BALANCES_ABI from 'single-call-balance-checker-abi'
import { MAINNET_CHAIN_ID, RINKEBY_CHAIN_ID, ROPSTEN_CHAIN_ID, KOVAN_CHAIN_ID } from '../controllers/network/enums'
import {
MAINNET_CHAIN_ID,
RINKEBY_CHAIN_ID,
ROPSTEN_CHAIN_ID,
KOVAN_CHAIN_ID,
} from '../controllers/network/enums'
import {
SINGLE_CALL_BALANCES_ADDRESS,
@ -42,7 +47,6 @@ import { bnToHex } from './util'
*
*/
export default class AccountTracker {
/**
* @param {Object} opts - Options for initializing the controller
* @param {Object} opts.provider - An EIP-1193 provider instance that uses the current global network
@ -205,19 +209,31 @@ export default class AccountTracker {
switch (chainId) {
case MAINNET_CHAIN_ID:
await this._updateAccountsViaBalanceChecker(addresses, SINGLE_CALL_BALANCES_ADDRESS)
await this._updateAccountsViaBalanceChecker(
addresses,
SINGLE_CALL_BALANCES_ADDRESS,
)
break
case RINKEBY_CHAIN_ID:
await this._updateAccountsViaBalanceChecker(addresses, SINGLE_CALL_BALANCES_ADDRESS_RINKEBY)
await this._updateAccountsViaBalanceChecker(
addresses,
SINGLE_CALL_BALANCES_ADDRESS_RINKEBY,
)
break
case ROPSTEN_CHAIN_ID:
await this._updateAccountsViaBalanceChecker(addresses, SINGLE_CALL_BALANCES_ADDRESS_ROPSTEN)
await this._updateAccountsViaBalanceChecker(
addresses,
SINGLE_CALL_BALANCES_ADDRESS_ROPSTEN,
)
break
case KOVAN_CHAIN_ID:
await this._updateAccountsViaBalanceChecker(addresses, SINGLE_CALL_BALANCES_ADDRESS_KOVAN)
await this._updateAccountsViaBalanceChecker(
addresses,
SINGLE_CALL_BALANCES_ADDRESS_KOVAN,
)
break
default:
@ -255,12 +271,17 @@ export default class AccountTracker {
async _updateAccountsViaBalanceChecker(addresses, deployedContractAddress) {
const { accounts } = this.store.getState()
this.web3.setProvider(this._provider)
const ethContract = this.web3.eth.contract(SINGLE_CALL_BALANCES_ABI).at(deployedContractAddress)
const ethContract = this.web3.eth
.contract(SINGLE_CALL_BALANCES_ABI)
.at(deployedContractAddress)
const ethBalance = ['0x0']
ethContract.balances(addresses, ethBalance, (error, result) => {
if (error) {
log.warn(`MetaMask - Account Tracker single call balance fetch failed`, error)
log.warn(
`MetaMask - Account Tracker single call balance fetch failed`,
error,
)
Promise.all(addresses.map(this._updateAccount.bind(this)))
return
}
@ -271,5 +292,4 @@ export default class AccountTracker {
this.store.updateState({ accounts })
})
}
}

View File

@ -46,6 +46,8 @@ function getDefaultServiceForNetwork (network) {
case '5':
return 'goerli-faucet'
default:
throw new Error(`No default cryptocurrency exchange or faucet for networkId: "${network}"`)
throw new Error(
`No default cryptocurrency exchange or faucet for networkId: "${network}"`,
)
}
}

View File

@ -5,10 +5,10 @@
*/
export default function cleanErrorStack(err) {
let { name } = err
name = (name === undefined) ? 'Error' : String(name)
name = name === undefined ? 'Error' : String(name)
let msg = err.message
msg = (msg === undefined) ? '' : String(msg)
msg = msg === undefined ? '' : String(msg)
if (name === '') {
err.stack = err.message

View File

@ -6,7 +6,11 @@ import log from 'loglevel'
* @returns {Function}
*/
export default function createLoggerMiddleware(opts) {
return function loggerMiddleware (/** @type {any} */ req, /** @type {any} */ res, /** @type {Function} */ next) {
return function loggerMiddleware(
/** @type {any} */ req,
/** @type {any} */ res,
/** @type {Function} */ next,
) {
next((/** @type {Function} */ cb) => {
if (res.error) {
log.error('Error in RPC response:\n', res)

View File

@ -6,7 +6,10 @@ import extension from 'extensionizer'
* @param {{ location: string, registerOnboarding: Function }} opts - The middleware options
* @returns {(req: any, res: any, next: Function, end: Function) => void}
*/
export default function createOnboardingMiddleware ({ location, registerOnboarding }) {
export default function createOnboardingMiddleware({
location,
registerOnboarding,
}) {
return async function originMiddleware(req, res, next, end) {
try {
if (req.method !== 'wallet_registerOnboarding') {
@ -16,7 +19,9 @@ export default function createOnboardingMiddleware ({ location, registerOnboardi
if (req.tabId && req.tabId !== extension.tabs.TAB_ID_NONE) {
await registerOnboarding(location, req.tabId)
} else {
log.debug(`'wallet_registerOnboarding' message from ${location} ignored due to missing tabId`)
log.debug(
`'wallet_registerOnboarding' message from ${location} ignored due to missing tabId`,
)
}
res.result = true
end()

View File

@ -4,7 +4,11 @@
* @returns {Function}
*/
export default function createOriginMiddleware(opts) {
return function originMiddleware (/** @type {any} */ req, /** @type {any} */ _, /** @type {Function} */ next) {
return function originMiddleware(
/** @type {any} */ req,
/** @type {any} */ _,
/** @type {Function} */ next,
) {
req.origin = opts.origin
next()
}

View File

@ -2,7 +2,6 @@ import { Writable as WritableStream } from 'readable-stream'
import promiseToCallback from 'promise-to-callback'
class AsyncWritableStream extends WritableStream {
constructor(asyncWriteFn, _opts) {
const opts = { objectMode: true, ..._opts }
super(opts)
@ -13,7 +12,6 @@ class AsyncWritableStream extends WritableStream {
_write(chunk, encoding, callback) {
promiseToCallback(this._asyncWriteFn(chunk, encoding))(callback)
}
}
export default function createStreamSink(asyncWriteFn, _opts) {

View File

@ -4,7 +4,11 @@
* @returns {Function}
*/
export default function createTabIdMiddleware(opts) {
return function tabIdMiddleware (/** @type {any} */ req, /** @type {any} */ _, /** @type {Function} */ next) {
return function tabIdMiddleware(
/** @type {any} */ req,
/** @type {any} */ _,
/** @type {Function} */ next,
) {
req.tabId = opts.tabId
next()
}

View File

@ -6,7 +6,7 @@ import log from 'loglevel'
import createId from './random-id'
import { MESSAGE_TYPE } from './enums'
const hexRe = /^[0-9A-Fa-f]+$/ug
const hexRe = /^[0-9A-Fa-f]+$/gu
/**
* Represents, and contains data about, an 'eth_decrypt' type decryption request. These are created when a
@ -26,7 +26,6 @@ const hexRe = /^[0-9A-Fa-f]+$/ug
*/
export default class DecryptMessageManager extends EventEmitter {
/**
* Controller in charge of managing - storing, adding, removing, updating - DecryptMessage.
*
@ -64,7 +63,8 @@ export default class DecryptMessageManager extends EventEmitter {
*
*/
getUnapprovedMsgs() {
return this.messages.filter((msg) => msg.status === 'unapproved')
return this.messages
.filter((msg) => msg.status === 'unapproved')
.reduce((result, msg) => {
result[msg.id] = msg
return result
@ -94,13 +94,23 @@ export default class DecryptMessageManager extends EventEmitter {
resolve(data.rawData)
return
case 'rejected':
reject(ethErrors.provider.userRejectedRequest('MetaMask Decryption: User denied message decryption.'))
reject(
ethErrors.provider.userRejectedRequest(
'MetaMask Decryption: User denied message decryption.',
),
)
return
case 'errored':
reject(new Error('This message cannot be decrypted'))
return
default:
reject(new Error(`MetaMask Decryption: Unknown problem: ${JSON.stringify(msgParams)}`))
reject(
new Error(
`MetaMask Decryption: Unknown problem: ${JSON.stringify(
msgParams,
)}`,
),
)
}
})
})
@ -117,14 +127,18 @@ export default class DecryptMessageManager extends EventEmitter {
*
*/
addUnapprovedMessage(msgParams, req) {
log.debug(`DecryptMessageManager addUnapprovedMessage: ${JSON.stringify(msgParams)}`)
log.debug(
`DecryptMessageManager addUnapprovedMessage: ${JSON.stringify(
msgParams,
)}`,
)
// add origin from request
if (req) {
msgParams.origin = req.origin
}
msgParams.data = this.normalizeMsgData(msgParams.data)
// create txData obj with parameters and meta data
const time = (new Date()).getTime()
const time = new Date().getTime()
const msgId = createId()
const msgData = {
id: msgId,
@ -254,12 +268,18 @@ export default class DecryptMessageManager extends EventEmitter {
_setMsgStatus(msgId, status) {
const msg = this.getMsg(msgId)
if (!msg) {
throw new Error(`DecryptMessageManager - Message not found for id: "${msgId}".`)
throw new Error(
`DecryptMessageManager - Message not found for id: "${msgId}".`,
)
}
msg.status = status
this._updateMsg(msg)
this.emit(`${msgId}:${status}`, msg)
if (status === 'rejected' || status === 'decrypted' || status === 'errored') {
if (
status === 'rejected' ||
status === 'decrypted' ||
status === 'errored'
) {
this.emit(`${msgId}:finished`, msg)
}
}
@ -291,7 +311,10 @@ export default class DecryptMessageManager extends EventEmitter {
_saveMsgList() {
const unapprovedDecryptMsgs = this.getUnapprovedMsgs()
const unapprovedDecryptMsgCount = Object.keys(unapprovedDecryptMsgs).length
this.memStore.updateState({ unapprovedDecryptMsgs, unapprovedDecryptMsgCount })
this.memStore.updateState({
unapprovedDecryptMsgs,
unapprovedDecryptMsgCount,
})
this.emit('updateBadge')
}
@ -314,5 +337,4 @@ export default class DecryptMessageManager extends EventEmitter {
return ethUtil.bufferToHex(Buffer.from(data, 'utf8'))
}
}

View File

@ -23,7 +23,6 @@ import { MESSAGE_TYPE } from './enums'
*/
export default class EncryptionPublicKeyManager extends EventEmitter {
/**
* Controller in charge of managing - storing, adding, removing, updating - EncryptionPublicKey.
*
@ -61,7 +60,8 @@ export default class EncryptionPublicKeyManager extends EventEmitter {
*
*/
getUnapprovedMsgs() {
return this.messages.filter((msg) => msg.status === 'unapproved')
return this.messages
.filter((msg) => msg.status === 'unapproved')
.reduce((result, msg) => {
result[msg.id] = msg
return result
@ -91,10 +91,20 @@ export default class EncryptionPublicKeyManager extends EventEmitter {
resolve(data.rawData)
return
case 'rejected':
reject(ethErrors.provider.userRejectedRequest('MetaMask EncryptionPublicKey: User denied message EncryptionPublicKey.'))
reject(
ethErrors.provider.userRejectedRequest(
'MetaMask EncryptionPublicKey: User denied message EncryptionPublicKey.',
),
)
return
default:
reject(new Error(`MetaMask EncryptionPublicKey: Unknown problem: ${JSON.stringify(address)}`))
reject(
new Error(
`MetaMask EncryptionPublicKey: Unknown problem: ${JSON.stringify(
address,
)}`,
),
)
}
})
})
@ -113,7 +123,7 @@ export default class EncryptionPublicKeyManager extends EventEmitter {
addUnapprovedMessage(address, req) {
log.debug(`EncryptionPublicKeyManager addUnapprovedMessage: address`)
// create txData obj with parameters and meta data
const time = (new Date()).getTime()
const time = new Date().getTime()
const msgId = createId()
const msgData = {
id: msgId,
@ -248,7 +258,9 @@ export default class EncryptionPublicKeyManager extends EventEmitter {
_setMsgStatus(msgId, status) {
const msg = this.getMsg(msgId)
if (!msg) {
throw new Error(`EncryptionPublicKeyManager - Message not found for id: "${msgId}".`)
throw new Error(
`EncryptionPublicKeyManager - Message not found for id: "${msgId}".`,
)
}
msg.status = status
this._updateMsg(msg)
@ -284,8 +296,13 @@ export default class EncryptionPublicKeyManager extends EventEmitter {
*/
_saveMsgList() {
const unapprovedEncryptionPublicKeyMsgs = this.getUnapprovedMsgs()
const unapprovedEncryptionPublicKeyMsgCount = Object.keys(unapprovedEncryptionPublicKeyMsgs).length
this.memStore.updateState({ unapprovedEncryptionPublicKeyMsgs, unapprovedEncryptionPublicKeyMsgCount })
const unapprovedEncryptionPublicKeyMsgCount = Object.keys(
unapprovedEncryptionPublicKeyMsgs,
).length
this.memStore.updateState({
unapprovedEncryptionPublicKeyMsgs,
unapprovedEncryptionPublicKeyMsgCount,
})
this.emit('updateBadge')
}
}

View File

@ -1,2 +1,109 @@
const abi = [{ 'constant': true, 'inputs': [{ 'name': 'node', 'type': 'bytes32' }], 'name': 'resolver', 'outputs': [{ 'name': '', 'type': 'address' }], 'payable': false, 'type': 'function' }, { 'constant': true, 'inputs': [{ 'name': 'node', 'type': 'bytes32' }], 'name': 'owner', 'outputs': [{ 'name': '', 'type': 'address' }], 'payable': false, 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': 'node', 'type': 'bytes32' }, { 'name': 'label', 'type': 'bytes32' }, { 'name': 'owner', 'type': 'address' }], 'name': 'setSubnodeOwner', 'outputs': [], 'payable': false, 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': 'node', 'type': 'bytes32' }, { 'name': 'ttl', 'type': 'uint64' }], 'name': 'setTTL', 'outputs': [], 'payable': false, 'type': 'function' }, { 'constant': true, 'inputs': [{ 'name': 'node', 'type': 'bytes32' }], 'name': 'ttl', 'outputs': [{ 'name': '', 'type': 'uint64' }], 'payable': false, 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': 'node', 'type': 'bytes32' }, { 'name': 'resolver', 'type': 'address' }], 'name': 'setResolver', 'outputs': [], 'payable': false, 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': 'node', 'type': 'bytes32' }, { 'name': 'owner', 'type': 'address' }], 'name': 'setOwner', 'outputs': [], 'payable': false, 'type': 'function' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'node', 'type': 'bytes32' }, { 'indexed': false, 'name': 'owner', 'type': 'address' }], 'name': 'Transfer', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'node', 'type': 'bytes32' }, { 'indexed': true, 'name': 'label', 'type': 'bytes32' }, { 'indexed': false, 'name': 'owner', 'type': 'address' }], 'name': 'NewOwner', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'node', 'type': 'bytes32' }, { 'indexed': false, 'name': 'resolver', 'type': 'address' }], 'name': 'NewResolver', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'node', 'type': 'bytes32' }, { 'indexed': false, 'name': 'ttl', 'type': 'uint64' }], 'name': 'NewTTL', 'type': 'event' }]
const abi = [
{
constant: true,
inputs: [{ name: 'node', type: 'bytes32' }],
name: 'resolver',
outputs: [{ name: '', type: 'address' }],
payable: false,
type: 'function',
},
{
constant: true,
inputs: [{ name: 'node', type: 'bytes32' }],
name: 'owner',
outputs: [{ name: '', type: 'address' }],
payable: false,
type: 'function',
},
{
constant: false,
inputs: [
{ name: 'node', type: 'bytes32' },
{ name: 'label', type: 'bytes32' },
{ name: 'owner', type: 'address' },
],
name: 'setSubnodeOwner',
outputs: [],
payable: false,
type: 'function',
},
{
constant: false,
inputs: [
{ name: 'node', type: 'bytes32' },
{ name: 'ttl', type: 'uint64' },
],
name: 'setTTL',
outputs: [],
payable: false,
type: 'function',
},
{
constant: true,
inputs: [{ name: 'node', type: 'bytes32' }],
name: 'ttl',
outputs: [{ name: '', type: 'uint64' }],
payable: false,
type: 'function',
},
{
constant: false,
inputs: [
{ name: 'node', type: 'bytes32' },
{ name: 'resolver', type: 'address' },
],
name: 'setResolver',
outputs: [],
payable: false,
type: 'function',
},
{
constant: false,
inputs: [
{ name: 'node', type: 'bytes32' },
{ name: 'owner', type: 'address' },
],
name: 'setOwner',
outputs: [],
payable: false,
type: 'function',
},
{
anonymous: false,
inputs: [
{ indexed: true, name: 'node', type: 'bytes32' },
{ indexed: false, name: 'owner', type: 'address' },
],
name: 'Transfer',
type: 'event',
},
{
anonymous: false,
inputs: [
{ indexed: true, name: 'node', type: 'bytes32' },
{ indexed: true, name: 'label', type: 'bytes32' },
{ indexed: false, name: 'owner', type: 'address' },
],
name: 'NewOwner',
type: 'event',
},
{
anonymous: false,
inputs: [
{ indexed: true, name: 'node', type: 'bytes32' },
{ indexed: false, name: 'resolver', type: 'address' },
],
name: 'NewResolver',
type: 'event',
},
{
anonymous: false,
inputs: [
{ indexed: true, name: 'node', type: 'bytes32' },
{ indexed: false, name: 'ttl', type: 'uint64' },
],
name: 'NewTTL',
type: 'event',
},
]
export default abi

View File

@ -1,2 +1,236 @@
const abi = [{ 'constant': false, 'inputs': [{ 'name': 'node', 'type': 'bytes32' }, { 'name': 'hash', 'type': 'bytes32' }], 'name': 'setContent', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function' }, { 'constant': true, 'inputs': [{ 'name': 'node', 'type': 'bytes32' }], 'name': 'content', 'outputs': [{ 'name': '', 'type': 'bytes32' }], 'payable': false, 'stateMutability': 'view', 'type': 'function' }, { 'constant': true, 'inputs': [{ 'name': 'interfaceID', 'type': 'bytes4' }], 'name': 'supportsInterface', 'outputs': [{ 'name': '', 'type': 'bool' }], 'payable': false, 'stateMutability': 'pure', 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': 'node', 'type': 'bytes32' }, { 'name': 'key', 'type': 'string' }, { 'name': 'value', 'type': 'string' }], 'name': 'setText', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function' }, { 'constant': true, 'inputs': [{ 'name': 'node', 'type': 'bytes32' }, { 'name': 'contentTypes', 'type': 'uint256' }], 'name': 'ABI', 'outputs': [{ 'name': 'contentType', 'type': 'uint256' }, { 'name': 'data', 'type': 'bytes' }], 'payable': false, 'stateMutability': 'view', 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': 'node', 'type': 'bytes32' }, { 'name': 'x', 'type': 'bytes32' }, { 'name': 'y', 'type': 'bytes32' }], 'name': 'setPubkey', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': 'node', 'type': 'bytes32' }, { 'name': 'hash', 'type': 'bytes' }], 'name': 'setContenthash', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function' }, { 'constant': true, 'inputs': [{ 'name': 'node', 'type': 'bytes32' }], 'name': 'addr', 'outputs': [{ 'name': '', 'type': 'address' }], 'payable': false, 'stateMutability': 'view', 'type': 'function' }, { 'constant': true, 'inputs': [{ 'name': 'node', 'type': 'bytes32' }, { 'name': 'key', 'type': 'string' }], 'name': 'text', 'outputs': [{ 'name': '', 'type': 'string' }], 'payable': false, 'stateMutability': 'view', 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': 'node', 'type': 'bytes32' }, { 'name': 'contentType', 'type': 'uint256' }, { 'name': 'data', 'type': 'bytes' }], 'name': 'setABI', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function' }, { 'constant': true, 'inputs': [{ 'name': 'node', 'type': 'bytes32' }], 'name': 'name', 'outputs': [{ 'name': '', 'type': 'string' }], 'payable': false, 'stateMutability': 'view', 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': 'node', 'type': 'bytes32' }, { 'name': 'name', 'type': 'string' }], 'name': 'setName', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function' }, { 'constant': true, 'inputs': [{ 'name': 'node', 'type': 'bytes32' }], 'name': 'contenthash', 'outputs': [{ 'name': '', 'type': 'bytes' }], 'payable': false, 'stateMutability': 'view', 'type': 'function' }, { 'constant': true, 'inputs': [{ 'name': 'node', 'type': 'bytes32' }], 'name': 'pubkey', 'outputs': [{ 'name': 'x', 'type': 'bytes32' }, { 'name': 'y', 'type': 'bytes32' }], 'payable': false, 'stateMutability': 'view', 'type': 'function' }, { 'constant': false, 'inputs': [{ 'name': 'node', 'type': 'bytes32' }, { 'name': 'addr', 'type': 'address' }], 'name': 'setAddr', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function' }, { 'inputs': [{ 'name': 'ensAddr', 'type': 'address' }], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'constructor' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'node', 'type': 'bytes32' }, { 'indexed': false, 'name': 'a', 'type': 'address' }], 'name': 'AddrChanged', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'node', 'type': 'bytes32' }, { 'indexed': false, 'name': 'name', 'type': 'string' }], 'name': 'NameChanged', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'node', 'type': 'bytes32' }, { 'indexed': true, 'name': 'contentType', 'type': 'uint256' }], 'name': 'ABIChanged', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'node', 'type': 'bytes32' }, { 'indexed': false, 'name': 'x', 'type': 'bytes32' }, { 'indexed': false, 'name': 'y', 'type': 'bytes32' }], 'name': 'PubkeyChanged', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'node', 'type': 'bytes32' }, { 'indexed': false, 'name': 'indexedKey', 'type': 'string' }, { 'indexed': false, 'name': 'key', 'type': 'string' }], 'name': 'TextChanged', 'type': 'event' }, { 'anonymous': false, 'inputs': [{ 'indexed': true, 'name': 'node', 'type': 'bytes32' }, { 'indexed': false, 'name': 'hash', 'type': 'bytes' }], 'name': 'ContenthashChanged', 'type': 'event' }]
const abi = [
{
constant: false,
inputs: [
{ name: 'node', type: 'bytes32' },
{ name: 'hash', type: 'bytes32' },
],
name: 'setContent',
outputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'function',
},
{
constant: true,
inputs: [{ name: 'node', type: 'bytes32' }],
name: 'content',
outputs: [{ name: '', type: 'bytes32' }],
payable: false,
stateMutability: 'view',
type: 'function',
},
{
constant: true,
inputs: [{ name: 'interfaceID', type: 'bytes4' }],
name: 'supportsInterface',
outputs: [{ name: '', type: 'bool' }],
payable: false,
stateMutability: 'pure',
type: 'function',
},
{
constant: false,
inputs: [
{ name: 'node', type: 'bytes32' },
{ name: 'key', type: 'string' },
{ name: 'value', type: 'string' },
],
name: 'setText',
outputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'function',
},
{
constant: true,
inputs: [
{ name: 'node', type: 'bytes32' },
{ name: 'contentTypes', type: 'uint256' },
],
name: 'ABI',
outputs: [
{ name: 'contentType', type: 'uint256' },
{ name: 'data', type: 'bytes' },
],
payable: false,
stateMutability: 'view',
type: 'function',
},
{
constant: false,
inputs: [
{ name: 'node', type: 'bytes32' },
{ name: 'x', type: 'bytes32' },
{ name: 'y', type: 'bytes32' },
],
name: 'setPubkey',
outputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'function',
},
{
constant: false,
inputs: [
{ name: 'node', type: 'bytes32' },
{ name: 'hash', type: 'bytes' },
],
name: 'setContenthash',
outputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'function',
},
{
constant: true,
inputs: [{ name: 'node', type: 'bytes32' }],
name: 'addr',
outputs: [{ name: '', type: 'address' }],
payable: false,
stateMutability: 'view',
type: 'function',
},
{
constant: true,
inputs: [
{ name: 'node', type: 'bytes32' },
{ name: 'key', type: 'string' },
],
name: 'text',
outputs: [{ name: '', type: 'string' }],
payable: false,
stateMutability: 'view',
type: 'function',
},
{
constant: false,
inputs: [
{ name: 'node', type: 'bytes32' },
{ name: 'contentType', type: 'uint256' },
{ name: 'data', type: 'bytes' },
],
name: 'setABI',
outputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'function',
},
{
constant: true,
inputs: [{ name: 'node', type: 'bytes32' }],
name: 'name',
outputs: [{ name: '', type: 'string' }],
payable: false,
stateMutability: 'view',
type: 'function',
},
{
constant: false,
inputs: [
{ name: 'node', type: 'bytes32' },
{ name: 'name', type: 'string' },
],
name: 'setName',
outputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'function',
},
{
constant: true,
inputs: [{ name: 'node', type: 'bytes32' }],
name: 'contenthash',
outputs: [{ name: '', type: 'bytes' }],
payable: false,
stateMutability: 'view',
type: 'function',
},
{
constant: true,
inputs: [{ name: 'node', type: 'bytes32' }],
name: 'pubkey',
outputs: [
{ name: 'x', type: 'bytes32' },
{ name: 'y', type: 'bytes32' },
],
payable: false,
stateMutability: 'view',
type: 'function',
},
{
constant: false,
inputs: [
{ name: 'node', type: 'bytes32' },
{ name: 'addr', type: 'address' },
],
name: 'setAddr',
outputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [{ name: 'ensAddr', type: 'address' }],
payable: false,
stateMutability: 'nonpayable',
type: 'constructor',
},
{
anonymous: false,
inputs: [
{ indexed: true, name: 'node', type: 'bytes32' },
{ indexed: false, name: 'a', type: 'address' },
],
name: 'AddrChanged',
type: 'event',
},
{
anonymous: false,
inputs: [
{ indexed: true, name: 'node', type: 'bytes32' },
{ indexed: false, name: 'name', type: 'string' },
],
name: 'NameChanged',
type: 'event',
},
{
anonymous: false,
inputs: [
{ indexed: true, name: 'node', type: 'bytes32' },
{ indexed: true, name: 'contentType', type: 'uint256' },
],
name: 'ABIChanged',
type: 'event',
},
{
anonymous: false,
inputs: [
{ indexed: true, name: 'node', type: 'bytes32' },
{ indexed: false, name: 'x', type: 'bytes32' },
{ indexed: false, name: 'y', type: 'bytes32' },
],
name: 'PubkeyChanged',
type: 'event',
},
{
anonymous: false,
inputs: [
{ indexed: true, name: 'node', type: 'bytes32' },
{ indexed: false, name: 'indexedKey', type: 'string' },
{ indexed: false, name: 'key', type: 'string' },
],
name: 'TextChanged',
type: 'event',
},
{
anonymous: false,
inputs: [
{ indexed: true, name: 'node', type: 'bytes32' },
{ indexed: false, name: 'hash', type: 'bytes' },
],
name: 'ContenthashChanged',
type: 'event',
},
]
export default abi

View File

@ -13,7 +13,9 @@ export default async function resolveEnsToIpfsContentId ({ provider, name }) {
const chainId = Number.parseInt(await eth.net_version(), 10)
const registryAddress = getRegistryForChainId(chainId)
if (!registryAddress) {
throw new Error(`EnsIpfsResolver - no known ens-ipfs registry for chainId "${chainId}"`)
throw new Error(
`EnsIpfsResolver - no known ens-ipfs registry for chainId "${chainId}"`,
)
}
const Registry = contract(registryAbi).at(registryAddress)
// lookup resolver
@ -33,7 +35,9 @@ export default async function resolveEnsToIpfsContentId ({ provider, name }) {
const type = contentHash.getCodec(rawContentHash)
if (type === 'ipfs-ns' || type === 'ipns-ns') {
decodedContentHash = contentHash.helpers.cidV0ToV1Base32(decodedContentHash)
decodedContentHash = contentHash.helpers.cidV0ToV1Base32(
decodedContentHash,
)
}
return { type, hash: decodedContentHash }
@ -43,15 +47,25 @@ export default async function resolveEnsToIpfsContentId ({ provider, name }) {
const contentLookupResult = await Resolver.content(hash)
const content = contentLookupResult[0]
if (hexValueIsEmpty(content)) {
throw new Error(`EnsIpfsResolver - no content ID found for name "${name}"`)
throw new Error(
`EnsIpfsResolver - no content ID found for name "${name}"`,
)
}
return { type: 'swarm-ns', hash: content.slice(2) }
}
throw new Error(`EnsIpfsResolver - the resolver for name "${name}" is not standard, it should either supports contenthash() or content()`)
throw new Error(
`EnsIpfsResolver - the resolver for name "${name}" is not standard, it should either supports contenthash() or content()`,
)
}
function hexValueIsEmpty(value) {
return [undefined, null, '0x', '0x0', '0x0000000000000000000000000000000000000000000000000000000000000000'].includes(value)
return [
undefined,
null,
'0x',
'0x0',
'0x0000000000000000000000000000000000000000000000000000000000000000',
].includes(value)
}
/**

View File

@ -3,11 +3,17 @@ import resolveEnsToIpfsContentId from './resolver'
const supportedTopLevelDomains = ['eth']
export default function setupEnsIpfsResolver ({ provider, getCurrentNetwork, getIpfsGateway }) {
export default function setupEnsIpfsResolver({
provider,
getCurrentNetwork,
getIpfsGateway,
}) {
// install listener
const urlPatterns = supportedTopLevelDomains.map((tld) => `*://*.${tld}/*`)
extension.webRequest.onErrorOccurred.addListener(webRequestDidFail, { urls: urlPatterns, types: ['main_frame'] })
extension.webRequest.onErrorOccurred.addListener(webRequestDidFail, {
urls: urlPatterns,
types: ['main_frame'],
})
// return api object
return {
@ -43,7 +49,10 @@ export default function setupEnsIpfsResolver ({ provider, getCurrentNetwork, get
try {
const { type, hash } = await resolveEnsToIpfsContentId({ provider, name })
if (type === 'ipfs-ns' || type === 'ipns-ns') {
const resolvedUrl = `https://${hash}.${type.slice(0, 4)}.${ipfsGateway}${pathname}${search || ''}${fragment || ''}`
const resolvedUrl = `https://${hash}.${type.slice(
0,
4,
)}.${ipfsGateway}${pathname}${search || ''}${fragment || ''}`
try {
// check if ipfs gateway has result
const response = await window.fetch(resolvedUrl, { method: 'HEAD' })
@ -54,11 +63,15 @@ export default function setupEnsIpfsResolver ({ provider, getCurrentNetwork, get
console.warn(err)
}
} else if (type === 'swarm-ns') {
url = `https://swarm-gateways.net/bzz:/${hash}${pathname}${search || ''}${fragment || ''}`
url = `https://swarm-gateways.net/bzz:/${hash}${pathname}${
search || ''
}${fragment || ''}`
} else if (type === 'onion' || type === 'onion3') {
url = `http://${hash}.onion${pathname}${search || ''}${fragment || ''}`
} else if (type === 'zeronet') {
url = `http://127.0.0.1:43110/${hash}${pathname}${search || ''}${fragment || ''}`
url = `http://127.0.0.1:43110/${hash}${pathname}${search || ''}${
fragment || ''
}`
}
} catch (err) {
console.warn(err)

View File

@ -17,7 +17,9 @@ export default function extractEthjsErrorMessage (errorMessage) {
const isEthjsRpcError = errorMessage.includes(ethJsRpcSlug)
if (isEthjsRpcError) {
const payloadAndError = errorMessage.slice(ethJsRpcSlug.length)
const originalError = payloadAndError.slice(payloadAndError.indexOf(errorLabelPrefix) + errorLabelPrefix.length)
const originalError = payloadAndError.slice(
payloadAndError.indexOf(errorLabelPrefix) + errorLabelPrefix.length,
)
return originalError
}
return errorMessage

View File

@ -1,13 +1,9 @@
/**
* Freezes the Promise global and prevents its reassignment.
*/
import deepFreeze from 'deep-freeze-strict'
if (
process.env.IN_TEST !== 'true' &&
process.env.METAMASK_ENV !== 'test'
) {
if (process.env.IN_TEST !== 'true' && process.env.METAMASK_ENV !== 'test') {
freeze(global, 'Promise')
}
@ -25,9 +21,9 @@ if (
* @param {boolean} [enumerable=true] - If given a value, whether the property is enumerable.
*/
function freeze(target, key, value, enumerable = true) {
const opts = {
configurable: false, writable: false,
configurable: false,
writable: false,
}
if (value === undefined) {

View File

@ -2,16 +2,16 @@ import extension from 'extensionizer'
import promisify from 'pify'
import allLocales from '../../_locales/index.json'
const getPreferredLocales = extension.i18n ? promisify(
extension.i18n.getAcceptLanguages,
{ errorFirst: false },
) : async () => []
const getPreferredLocales = extension.i18n
? promisify(extension.i18n.getAcceptLanguages, { errorFirst: false })
: async () => []
// mapping some browsers return hyphen instead underscore in locale codes (e.g. zh_TW -> zh-tw)
const existingLocaleCodes = {}
allLocales.forEach((locale) => {
if (locale && locale.code) {
existingLocaleCodes[locale.code.toLowerCase().replace('_', '-')] = locale.code
existingLocaleCodes[locale.code.toLowerCase().replace('_', '-')] =
locale.code
}
})
@ -40,7 +40,9 @@ export default async function getFirstPreferredLangCode () {
const firstPreferredLangCode = userPreferredLocaleCodes
.map((code) => code.toLowerCase().replace('_', '-'))
.find((code) => Object.prototype.hasOwnProperty.call(existingLocaleCodes, code))
.find((code) =>
Object.prototype.hasOwnProperty.call(existingLocaleCodes, code),
)
return existingLocaleCodes[firstPreferredLangCode] || 'en'
}

View File

@ -6,7 +6,6 @@ import { checkForError } from './util'
* A wrapper around the extension's storage local API
*/
export default class ExtensionStore {
/**
* @constructor
*/

View File

@ -24,7 +24,6 @@ import { MESSAGE_TYPE } from './enums'
*/
export default class MessageManager extends EventEmitter {
/**
* Controller in charge of managing - storing, adding, removing, updating - Messages.
*
@ -62,7 +61,8 @@ export default class MessageManager extends EventEmitter {
*
*/
getUnapprovedMsgs() {
return this.messages.filter((msg) => msg.status === 'unapproved')
return this.messages
.filter((msg) => msg.status === 'unapproved')
.reduce((result, msg) => {
result[msg.id] = msg
return result
@ -87,9 +87,19 @@ export default class MessageManager extends EventEmitter {
case 'signed':
return resolve(data.rawSig)
case 'rejected':
return reject(ethErrors.provider.userRejectedRequest('MetaMask Message Signature: User denied message signature.'))
return reject(
ethErrors.provider.userRejectedRequest(
'MetaMask Message Signature: User denied message signature.',
),
)
default:
return reject(new Error(`MetaMask Message Signature: Unknown problem: ${JSON.stringify(msgParams)}`))
return reject(
new Error(
`MetaMask Message Signature: Unknown problem: ${JSON.stringify(
msgParams,
)}`,
),
)
}
})
})
@ -111,7 +121,7 @@ export default class MessageManager extends EventEmitter {
}
msgParams.data = normalizeMsgData(msgParams.data)
// create txData obj with parameters and meta data
const time = (new Date()).getTime()
const time = new Date().getTime()
const msgId = createId()
const msgData = {
id: msgId,
@ -265,7 +275,6 @@ export default class MessageManager extends EventEmitter {
this.memStore.updateState({ unapprovedMsgs, unapprovedMsgCount })
this.emit('updateBadge')
}
}
/**

View File

@ -13,7 +13,6 @@ import EventEmitter from 'events'
*/
export default class Migrator extends EventEmitter {
/**
* @constructor
* @param {MigratorOptions} opts
@ -26,7 +25,8 @@ export default class Migrator extends EventEmitter {
// grab migration with highest version
const lastMigration = this.migrations.slice(-1)[0]
// use specified defaultVersion or highest migration version
this.defaultVersion = opts.defaultVersion || (lastMigration && lastMigration.version) || 0
this.defaultVersion =
opts.defaultVersion || (lastMigration && lastMigration.version) || 0
}
// run all pending migrations on meta in place
@ -42,8 +42,13 @@ export default class Migrator extends EventEmitter {
if (!migratedData.data) {
throw new Error('Migrator - migration returned empty data')
}
if (migratedData.version !== undefined && migratedData.meta.version !== migration.version) {
throw new Error('Migrator - Migration did not update version number correctly')
if (
migratedData.version !== undefined &&
migratedData.meta.version !== migration.version
) {
throw new Error(
'Migrator - Migration did not update version number correctly',
)
}
// accept the migration as good
// eslint-disable-next-line no-param-reassign
@ -87,5 +92,4 @@ export default class Migrator extends EventEmitter {
data,
}
}
}

View File

@ -4,7 +4,6 @@ const NOTIFICATION_HEIGHT = 620
const NOTIFICATION_WIDTH = 360
export default class NotificationManager {
/**
* A collection of methods for controlling the showing and hiding of the notification popup.
*
@ -84,10 +83,11 @@ export default class NotificationManager {
*
*/
_getPopupIn(windows) {
return windows ? windows.find((win) => {
return windows
? windows.find((win) => {
// Returns notification popup
return (win && win.type === 'popup' && win.id === this._popupId)
}) : null
return win && win.type === 'popup' && win.id === this._popupId
})
: null
}
}

View File

@ -6,7 +6,7 @@ import log from 'loglevel'
import createId from './random-id'
import { MESSAGE_TYPE } from './enums'
const hexRe = /^[0-9A-Fa-f]+$/ug
const hexRe = /^[0-9A-Fa-f]+$/gu
/**
* Represents, and contains data about, an 'personal_sign' type signature request. These are created when a
@ -28,7 +28,6 @@ const hexRe = /^[0-9A-Fa-f]+$/ug
*/
export default class PersonalMessageManager extends EventEmitter {
/**
* Controller in charge of managing - storing, adding, removing, updating - PersonalMessage.
*
@ -67,7 +66,8 @@ export default class PersonalMessageManager extends EventEmitter {
*
*/
getUnapprovedMsgs() {
return this.messages.filter((msg) => msg.status === 'unapproved')
return this.messages
.filter((msg) => msg.status === 'unapproved')
.reduce((result, msg) => {
result[msg.id] = msg
return result
@ -97,10 +97,20 @@ export default class PersonalMessageManager extends EventEmitter {
resolve(data.rawSig)
return
case 'rejected':
reject(ethErrors.provider.userRejectedRequest('MetaMask Message Signature: User denied message signature.'))
reject(
ethErrors.provider.userRejectedRequest(
'MetaMask Message Signature: User denied message signature.',
),
)
return
default:
reject(new Error(`MetaMask Message Signature: Unknown problem: ${JSON.stringify(msgParams)}`))
reject(
new Error(
`MetaMask Message Signature: Unknown problem: ${JSON.stringify(
msgParams,
)}`,
),
)
}
})
})
@ -117,14 +127,18 @@ export default class PersonalMessageManager extends EventEmitter {
*
*/
addUnapprovedMessage(msgParams, req) {
log.debug(`PersonalMessageManager addUnapprovedMessage: ${JSON.stringify(msgParams)}`)
log.debug(
`PersonalMessageManager addUnapprovedMessage: ${JSON.stringify(
msgParams,
)}`,
)
// add origin from request
if (req) {
msgParams.origin = req.origin
}
msgParams.data = this.normalizeMsgData(msgParams.data)
// create txData obj with parameters and meta data
const time = (new Date()).getTime()
const time = new Date().getTime()
const msgId = createId()
const msgData = {
id: msgId,
@ -241,7 +255,9 @@ export default class PersonalMessageManager extends EventEmitter {
_setMsgStatus(msgId, status) {
const msg = this.getMsg(msgId)
if (!msg) {
throw new Error(`PersonalMessageManager - Message not found for id: "${msgId}".`)
throw new Error(
`PersonalMessageManager - Message not found for id: "${msgId}".`,
)
}
msg.status = status
this._updateMsg(msg)
@ -277,8 +293,12 @@ export default class PersonalMessageManager extends EventEmitter {
*/
_saveMsgList() {
const unapprovedPersonalMsgs = this.getUnapprovedMsgs()
const unapprovedPersonalMsgCount = Object.keys(unapprovedPersonalMsgs).length
this.memStore.updateState({ unapprovedPersonalMsgs, unapprovedPersonalMsgCount })
const unapprovedPersonalMsgCount = Object.keys(unapprovedPersonalMsgs)
.length
this.memStore.updateState({
unapprovedPersonalMsgs,
unapprovedPersonalMsgCount,
})
this.emit('updateBadge')
}
@ -301,5 +321,4 @@ export default class PersonalMessageManager extends EventEmitter {
return ethUtil.bufferToHex(Buffer.from(data, 'utf8'))
}
}

View File

@ -1,6 +1,4 @@
import logWeb3Usage from './log-web3-usage'
const handlers = [
logWeb3Usage,
]
const handlers = [logWeb3Usage]
export default handlers

View File

@ -34,10 +34,7 @@ const recordedWeb3Usage = {}
* @param {Function} end - The json-rpc-engine 'end' callback.
* @param {LogWeb3UsageOptions} options
*/
function logWeb3UsageHandler (
req, res, _next, end,
{ origin, sendMetrics },
) {
function logWeb3UsageHandler(req, res, _next, end, { origin, sendMetrics }) {
const { action, name } = req.params[0]
if (!recordedWeb3Usage[origin]) {

View File

@ -2,7 +2,6 @@ import KeyringController from 'eth-keyring-controller'
import log from 'loglevel'
const seedPhraseVerifier = {
/**
* Verifies if the seed words can restore the accounts.
*
@ -39,8 +38,12 @@ const seedPhraseVerifier = {
}
for (let i = 0; i < restoredAccounts.length; i++) {
if (restoredAccounts[i].toLowerCase() !== createdAccounts[i].toLowerCase()) {
throw new Error(`Not identical accounts! Original: ${createdAccounts[i]}, Restored: ${restoredAccounts[i]}`)
if (
restoredAccounts[i].toLowerCase() !== createdAccounts[i].toLowerCase()
) {
throw new Error(
`Not identical accounts! Original: ${createdAccounts[i]}, Restored: ${restoredAccounts[i]}`,
)
}
}
},

View File

@ -18,8 +18,13 @@ export default function setupFetchDebugging () {
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')
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

View File

@ -8,7 +8,8 @@ import extractEthjsErrorMessage from './extractEthjsErrorMessage'
const METAMASK_DEBUG = process.env.METAMASK_DEBUG
const METAMASK_ENVIRONMENT = process.env.METAMASK_ENVIRONMENT
/* eslint-enable prefer-destructuring */
const SENTRY_DSN_DEV = 'https://f59f3dd640d2429d9d0e2445a87ea8e1@sentry.io/273496'
const SENTRY_DSN_DEV =
'https://f59f3dd640d2429d9d0e2445a87ea8e1@sentry.io/273496'
// This describes the subset of Redux state attached to errors sent to Sentry
// These properties have some potential to be useful for debugging, and they do
@ -73,12 +74,18 @@ export default function setupSentry ({ release, getState }) {
return undefined
} else if (METAMASK_ENVIRONMENT === 'production') {
if (!process.env.SENTRY_DSN) {
throw new Error(`Missing SENTRY_DSN environment variable in production environment`)
throw new Error(
`Missing SENTRY_DSN environment variable in production environment`,
)
}
console.log(`Setting up Sentry Remote Error Reporting for '${METAMASK_ENVIRONMENT}': SENTRY_DSN`)
console.log(
`Setting up Sentry Remote Error Reporting for '${METAMASK_ENVIRONMENT}': SENTRY_DSN`,
)
sentryTarget = process.env.SENTRY_DSN
} else {
console.log(`Setting up Sentry Remote Error Reporting for '${METAMASK_ENVIRONMENT}': SENTRY_DSN_DEV`)
console.log(
`Setting up Sentry Remote Error Reporting for '${METAMASK_ENVIRONMENT}': SENTRY_DSN_DEV`,
)
sentryTarget = SENTRY_DSN_DEV
}
@ -86,10 +93,7 @@ export default function setupSentry ({ release, getState }) {
dsn: sentryTarget,
debug: METAMASK_DEBUG,
environment: METAMASK_ENVIRONMENT,
integrations: [
new Dedupe(),
new ExtraErrorData(),
],
integrations: [new Dedupe(), new ExtraErrorData()],
release,
beforeSend: (report) => rewriteReport(report),
})
@ -122,7 +126,11 @@ function simplifyErrorMessages (report) {
// simplify ethjs error messages
let simplifiedErrorMessage = extractEthjsErrorMessage(errorMessage)
// simplify 'Transaction Failed: known transaction'
if (simplifiedErrorMessage.indexOf('Transaction Failed: known transaction') === 0) {
if (
simplifiedErrorMessage.indexOf(
'Transaction Failed: known transaction',
) === 0
) {
// cut the hash from the error message
simplifiedErrorMessage = 'Transaction Failed: known transaction'
}

View File

@ -5,11 +5,11 @@
import 'web3/dist/web3.min'
const shouldLogUsage = !([
const shouldLogUsage = ![
'docs.metamask.io',
'metamask.github.io',
'metamask.io',
].includes(window.location.hostname))
].includes(window.location.hostname)
export default function setupWeb3(log) {
// export web3 as a global, checking for usage
@ -33,13 +33,14 @@ export default function setupWeb3 (log) {
const web3Proxy = new Proxy(web3, {
get: (_web3, key) => {
// get the time of use
lastTimeUsed = Date.now()
// show warning once on web3 access
if (!hasBeenWarned) {
console.warn(`MetaMask: We will stop injecting web3 in Q4 2020.\nPlease see this article for more information: https://medium.com/metamask/no-longer-injecting-web3-js-4a899ad6e59e`)
console.warn(
`MetaMask: We will stop injecting web3 in Q4 2020.\nPlease see this article for more information: https://medium.com/metamask/no-longer-injecting-web3-js-4a899ad6e59e`,
)
hasBeenWarned = true
}
@ -129,7 +130,5 @@ function triggerReset () {
* @param {any} key - The key to stringify
*/
function stringifyKey(key) {
return typeof key === 'string'
? key
: `typeof ${typeof key}`
return typeof key === 'string' ? key : `typeof ${typeof key}`
}

View File

@ -8,15 +8,10 @@ import pump from 'pump'
*/
export function setupMultiplex(connectionStream) {
const mux = new ObjectMultiplex()
pump(
connectionStream,
mux,
connectionStream,
(err) => {
pump(connectionStream, mux, connectionStream, (err) => {
if (err) {
console.error(err)
}
},
)
})
return mux
}

View File

@ -28,7 +28,6 @@ import { MESSAGE_TYPE } from './enums'
*/
export default class TypedMessageManager extends EventEmitter {
/**
* Controller in charge of managing - storing, adding, removing, updating - TypedMessage.
*/
@ -60,7 +59,8 @@ export default class TypedMessageManager extends EventEmitter {
*
*/
getUnapprovedMsgs() {
return this.messages.filter((msg) => msg.status === 'unapproved')
return this.messages
.filter((msg) => msg.status === 'unapproved')
.reduce((result, msg) => {
result[msg.id] = msg
return result
@ -85,11 +85,23 @@ export default class TypedMessageManager extends EventEmitter {
case 'signed':
return resolve(data.rawSig)
case 'rejected':
return reject(ethErrors.provider.userRejectedRequest('MetaMask Message Signature: User denied message signature.'))
return reject(
ethErrors.provider.userRejectedRequest(
'MetaMask Message Signature: User denied message signature.',
),
)
case 'errored':
return reject(new Error(`MetaMask Message Signature: ${data.error}`))
return reject(
new Error(`MetaMask Message Signature: ${data.error}`),
)
default:
return reject(new Error(`MetaMask Message Signature: Unknown problem: ${JSON.stringify(msgParams)}`))
return reject(
new Error(
`MetaMask Message Signature: Unknown problem: ${JSON.stringify(
msgParams,
)}`,
),
)
}
})
})
@ -106,17 +118,18 @@ export default class TypedMessageManager extends EventEmitter {
*
*/
addUnapprovedMessage(msgParams, req, version) {
msgParams.version = version
if (req) {
msgParams.origin = req.origin
}
this.validateParams(msgParams)
log.debug(`TypedMessageManager addUnapprovedMessage: ${JSON.stringify(msgParams)}`)
log.debug(
`TypedMessageManager addUnapprovedMessage: ${JSON.stringify(msgParams)}`,
)
// create txData obj with parameters and meta data
const time = (new Date()).getTime()
const time = new Date().getTime()
const msgId = createId()
const msgData = {
id: msgId,
@ -139,7 +152,6 @@ export default class TypedMessageManager extends EventEmitter {
*
*/
validateParams(params) {
assert.ok(params && typeof params === 'object', 'Params must be an object.')
assert.ok('data' in params, 'Params must include a "data" field.')
assert.ok('from' in params, 'Params must include a "from" field.')
@ -157,19 +169,40 @@ export default class TypedMessageManager extends EventEmitter {
break
case 'V3':
case 'V4': {
assert.equal(typeof params.data, 'string', '"params.data" must be a string.')
assert.equal(
typeof params.data,
'string',
'"params.data" must be a string.',
)
let data
assert.doesNotThrow(() => {
data = JSON.parse(params.data)
}, '"data" must be a valid JSON string.')
const validation = jsonschema.validate(data, sigUtil.TYPED_MESSAGE_SCHEMA)
assert.ok(data.primaryType in data.types, `Primary type of "${data.primaryType}" has no type definition.`)
assert.equal(validation.errors.length, 0, 'Signing data must conform to EIP-712 schema. See https://git.io/fNtcx.')
const validation = jsonschema.validate(
data,
sigUtil.TYPED_MESSAGE_SCHEMA,
)
assert.ok(
data.primaryType in data.types,
`Primary type of "${data.primaryType}" has no type definition.`,
)
assert.equal(
validation.errors.length,
0,
'Signing data must conform to EIP-712 schema. See https://git.io/fNtcx.',
)
const { chainId } = data.domain
if (chainId) {
const activeChainId = parseInt(this._getCurrentChainId(), 16)
assert.ok(!Number.isNaN(activeChainId), `Cannot sign messages for chainId "${chainId}", because MetaMask is switching networks.`)
assert.equal(chainId, activeChainId, `Provided chainId "${chainId}" must match the active chainId "${activeChainId}"`)
assert.ok(
!Number.isNaN(activeChainId),
`Cannot sign messages for chainId "${chainId}", because MetaMask is switching networks.`,
)
assert.equal(
chainId,
activeChainId,
`Provided chainId "${chainId}" must match the active chainId "${activeChainId}"`,
)
}
break
}
@ -297,7 +330,9 @@ export default class TypedMessageManager extends EventEmitter {
_setMsgStatus(msgId, status) {
const msg = this.getMsg(msgId)
if (!msg) {
throw new Error(`TypedMessageManager - Message not found for id: "${msgId}".`)
throw new Error(
`TypedMessageManager - Message not found for id: "${msgId}".`,
)
}
msg.status = status
this._updateMsg(msg)
@ -333,9 +368,12 @@ export default class TypedMessageManager extends EventEmitter {
*/
_saveMsgList() {
const unapprovedTypedMessages = this.getUnapprovedMsgs()
const unapprovedTypedMessagesCount = Object.keys(unapprovedTypedMessages).length
this.memStore.updateState({ unapprovedTypedMessages, unapprovedTypedMessagesCount })
const unapprovedTypedMessagesCount = Object.keys(unapprovedTypedMessages)
.length
this.memStore.updateState({
unapprovedTypedMessages,
unapprovedTypedMessagesCount,
})
this.emit('updateBadge')
}
}

View File

@ -44,7 +44,8 @@ const getEnvironmentTypeMemo = memoize((url) => {
* @param {string} [url] - the URL of the window
* @returns {string} the environment ENUM
*/
const getEnvironmentType = (url = window.location.href) => getEnvironmentTypeMemo(url)
const getEnvironmentType = (url = window.location.href) =>
getEnvironmentTypeMemo(url)
/**
* Returns the platform (browser) where the extension is running.
@ -82,8 +83,16 @@ const getPlatform = (_) => {
*/
function sufficientBalance(txParams, hexBalance) {
// validate hexBalance is a hex string
assert.equal(typeof hexBalance, 'string', 'sufficientBalance - hexBalance is not a hex string')
assert.equal(hexBalance.slice(0, 2), '0x', 'sufficientBalance - hexBalance is not a hex string')
assert.equal(
typeof hexBalance,
'string',
'sufficientBalance - hexBalance is not a hex string',
)
assert.equal(
hexBalance.slice(0, 2),
'0x',
'sufficientBalance - hexBalance is not a hex string',
)
const balance = hexToBn(hexBalance)
const value = hexToBn(txParams.value)
@ -161,7 +170,7 @@ function isPrefixedFormattedHexString (value) {
if (typeof value !== 'string') {
return false
}
return (/^0x[1-9a-f]+[0-9a-f]*$/ui).test(value)
return /^0x[1-9a-f]+[0-9a-f]*$/iu.test(value)
}
export {

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,8 @@ export default {
try {
if (versionedData.data.config.provider.type === 'etherscan') {
versionedData.data.config.provider.type = 'rpc'
versionedData.data.config.provider.rpcTarget = 'https://rpc.metamask.io/'
versionedData.data.config.provider.rpcTarget =
'https://rpc.metamask.io/'
}
} catch (_) {
// empty

View File

@ -36,7 +36,9 @@ function transformState (state) {
if (!txMeta.err) {
return txMeta
}
if (txMeta.err === 'transaction with the same hash was already imported.') {
if (
txMeta.err === 'transaction with the same hash was already imported.'
) {
txMeta.status = 'submitted'
delete txMeta.err
}

View File

@ -42,13 +42,11 @@ function transformState (state) {
return txMeta
}
// has history: migrate
const newHistory = (
migrateFromSnapshotsToDiffs(txMeta.history)
const newHistory = migrateFromSnapshotsToDiffs(txMeta.history)
// remove empty diffs
.filter((entry) => {
return !Array.isArray(entry) || entry.length > 0
})
)
txMeta.history = newHistory
return txMeta
})

View File

@ -1,4 +1,3 @@
/*
This migration sets transactions as failed
@ -31,23 +30,32 @@ function transformState (state) {
const newState = state
const { TransactionController } = newState
if (TransactionController && TransactionController.transactions) {
const { transactions } = newState.TransactionController
newState.TransactionController.transactions = transactions.map((txMeta, _, txList) => {
newState.TransactionController.transactions = transactions.map(
(txMeta, _, txList) => {
if (txMeta.status !== 'submitted') {
return txMeta
}
const confirmedTxs = txList.filter((tx) => tx.status === 'confirmed')
const confirmedTxs = txList
.filter((tx) => tx.status === 'confirmed')
.filter((tx) => tx.txParams.from === txMeta.txParams.from)
.filter((tx) => tx.metamaskNetworkId.from === txMeta.metamaskNetworkId.from)
.filter(
(tx) => tx.metamaskNetworkId.from === txMeta.metamaskNetworkId.from,
)
const highestConfirmedNonce = getHighestNonce(confirmedTxs)
const pendingTxs = txList.filter((tx) => tx.status === 'submitted')
const pendingTxs = txList
.filter((tx) => tx.status === 'submitted')
.filter((tx) => tx.txParams.from === txMeta.txParams.from)
.filter((tx) => tx.metamaskNetworkId.from === txMeta.metamaskNetworkId.from)
const highestContinuousNonce = getHighestContinuousFrom(pendingTxs, highestConfirmedNonce)
.filter(
(tx) => tx.metamaskNetworkId.from === txMeta.metamaskNetworkId.from,
)
const highestContinuousNonce = getHighestContinuousFrom(
pendingTxs,
highestConfirmedNonce,
)
const maxNonce = Math.max(highestContinuousNonce, highestConfirmedNonce)
@ -59,7 +67,8 @@ function transformState (state) {
}
}
return txMeta
})
},
)
}
return newState
}
@ -86,4 +95,3 @@ function getHighestNonce (txList) {
const highestNonce = Math.max.apply(null, nonces)
return highestNonce
}

View File

@ -29,8 +29,7 @@ export default {
function transformState(state) {
const newState = state
if ('metamask' in newState &&
!('firstTimeInfo' in newState.metamask)) {
if ('metamask' in newState && !('firstTimeInfo' in newState.metamask)) {
newState.metamask.firstTimeInfo = {
version: '3.12.0',
date: Date.now(),
@ -38,4 +37,3 @@ function transformState (state) {
}
return newState
}

View File

@ -31,4 +31,3 @@ function transformState (state) {
delete newState.RecentBlocks
return newState
}

View File

@ -1,4 +1,3 @@
/*
This migration adds submittedTime to the txMeta if it is not their
@ -36,7 +35,7 @@ function transformState (state) {
if (txMeta.status !== 'submitted' || txMeta.submittedTime) {
return txMeta
}
txMeta.submittedTime = (new Date()).getTime()
txMeta.submittedTime = new Date().getTime()
return txMeta
})
}

View File

@ -1,4 +1,3 @@
/*
This migration removes transactions that are no longer usefull down to 40 total
@ -41,10 +40,12 @@ function transformState (state) {
let stripping = true
while (reverseTxList.length > 40 && stripping) {
const txIndex = reverseTxList.findIndex((txMeta) => {
return (txMeta.status === 'failed' ||
return (
txMeta.status === 'failed' ||
txMeta.status === 'rejected' ||
txMeta.status === 'confirmed' ||
txMeta.status === 'dropped')
txMeta.status === 'dropped'
)
})
if (txIndex < 0) {
stripping = false

View File

@ -1,4 +1,3 @@
/*
This migration ensures that the from address in txParams is to lower case for
@ -29,7 +28,8 @@ function transformState (state) {
return newState
}
const { transactions } = newState.TransactionController
newState.TransactionController.transactions = transactions.map((txMeta, _) => {
newState.TransactionController.transactions = transactions.map(
(txMeta, _) => {
if (
txMeta.status === 'unapproved' &&
txMeta.txParams &&
@ -38,6 +38,7 @@ function transformState (state) {
txMeta.txParams.from = txMeta.txParams.from.toLowerCase()
}
return txMeta
})
},
)
return newState
}

View File

@ -29,13 +29,15 @@ function transformState (state) {
if (newState.TransactionController) {
if (newState.TransactionController.transactions) {
const { transactions } = newState.TransactionController
newState.TransactionController.transactions = transactions.map((txMeta) => {
newState.TransactionController.transactions = transactions.map(
(txMeta) => {
if (txMeta.status !== 'unapproved') {
return txMeta
}
txMeta.txParams = normalizeTxParams(txMeta.txParams)
return txMeta
})
},
)
}
}

View File

@ -34,8 +34,9 @@ function transformState (state) {
return state
}
state.PreferencesController.identities = Object.keys(state.KeyringController.walletNicknames)
.reduce((identities, address) => {
state.PreferencesController.identities = Object.keys(
state.KeyringController.walletNicknames,
).reduce((identities, address) => {
identities[address] = {
name: state.KeyringController.walletNicknames[address],
address,

View File

@ -27,7 +27,9 @@ function transformState (state) {
if (newState.TransactionController) {
if (newState.TransactionController.transactions) {
const { transactions } = newState.TransactionController
newState.TransactionController.transactions = transactions.filter((txMeta) => txMeta.status !== 'rejected')
newState.TransactionController.transactions = transactions.filter(
(txMeta) => txMeta.status !== 'rejected',
)
}
}

View File

@ -25,11 +25,16 @@ function transformState (state) {
const newState = state
if (newState.PreferencesController) {
if (newState.PreferencesController.tokens && newState.PreferencesController.identities) {
if (
newState.PreferencesController.tokens &&
newState.PreferencesController.identities
) {
const { identities, tokens } = newState.PreferencesController
newState.PreferencesController.accountTokens = {}
Object.keys(identities).forEach((identity) => {
newState.PreferencesController.accountTokens[identity] = { 'mainnet': tokens }
newState.PreferencesController.accountTokens[identity] = {
mainnet: tokens,
}
})
newState.PreferencesController.tokens = []
}

View File

@ -18,10 +18,14 @@ normalizes txParams on unconfirmed txs
export default {
version,
migrate: failTxsThat(version, 'Stuck in approved state for too long.', (txMeta) => {
migrate: failTxsThat(
version,
'Stuck in approved state for too long.',
(txMeta) => {
const isApproved = txMeta.status === 'approved'
const createdTime = txMeta.submittedTime
const now = Date.now()
return isApproved && now - createdTime > unacceptableDelay
}),
},
),
}

View File

@ -37,13 +37,20 @@ function transformState (state) {
}
}
if (state.NetworkController) {
if (
newState.NetworkController.network &&
// eslint-disable-next-line radix
if (newState.NetworkController.network && Number.isNaN(parseInt(newState.NetworkController.network))) {
Number.isNaN(parseInt(newState.NetworkController.network))
) {
delete newState.NetworkController.network
}
if (
newState.NetworkController.provider &&
newState.NetworkController.provider.chainId &&
// eslint-disable-next-line radix
if (newState.NetworkController.provider && newState.NetworkController.provider.chainId && Number.isNaN(parseInt(newState.NetworkController.provider.chainId))) {
Number.isNaN(parseInt(newState.NetworkController.provider.chainId))
) {
delete newState.NetworkController.provider.chainId
}
}

View File

@ -23,7 +23,10 @@ function transformState (state) {
if (PreferencesController) {
const featureFlags = PreferencesController.featureFlags || {}
if (!featureFlags.privacyMode && typeof PreferencesController.migratedPrivacyMode === 'undefined') {
if (
!featureFlags.privacyMode &&
typeof PreferencesController.migratedPrivacyMode === 'undefined'
) {
// Mark the state has being migrated and enable Privacy Mode
PreferencesController.migratedPrivacyMode = true
featureFlags.privacyMode = true

View File

@ -21,7 +21,10 @@ export default {
}
function transformState(state) {
if (state.PreferencesController && state.PreferencesController.seedWords !== undefined) {
if (
state.PreferencesController &&
state.PreferencesController.seedWords !== undefined
) {
delete state.PreferencesController.seedWords
}
return state

View File

@ -20,7 +20,6 @@ export default {
}
function transformState(state) {
if (state.AddressBookController) {
const ab = state.AddressBookController.addressBook
@ -38,7 +37,6 @@ function transformState (state) {
newAddressBook[id] = {}
for (const address in ab) {
if (ab[address].chainId === id) {
ab[address].isEns = false
if (util.normalizeEnsName(ab[address].name)) {
ab[address].isEns = true

View File

@ -8,9 +8,12 @@ const DAI_V1_TOKEN_SYMBOL = 'DAI'
const SAI_TOKEN_SYMBOL = 'SAI'
function isOldDai(token = {}) {
return token && typeof token === 'object' &&
return (
token &&
typeof token === 'object' &&
token.symbol === DAI_V1_TOKEN_SYMBOL &&
ethUtil.toChecksumAddress(token.address) === DAI_V1_CONTRACT_ADDRESS
)
}
/**

View File

@ -18,7 +18,8 @@ export default {
function transformState(state) {
if (state.PreferencesController && state.PreferencesController.preferences) {
state.PreferencesController.preferences.autoLockTimeLimit = state.PreferencesController.preferences.autoLogoutTimeLimit
state.PreferencesController.preferences.autoLockTimeLimit =
state.PreferencesController.preferences.autoLogoutTimeLimit
delete state.PreferencesController.preferences.autoLogoutTimeLimit
}
return state

View File

@ -17,7 +17,10 @@ export default {
}
function transformState(state) {
if (typeof state?.AppStateController?.mkrMigrationReminderTimestamp !== 'undefined') {
if (
typeof state?.AppStateController?.mkrMigrationReminderTimestamp !==
'undefined'
) {
delete state.AppStateController.mkrMigrationReminderTimestamp
}
return state

View File

@ -16,10 +16,7 @@ export default {
},
}
const outdatedGateways = [
'ipfs.io',
'ipfs.dweb.link',
]
const outdatedGateways = ['ipfs.io', 'ipfs.dweb.link']
function transformState(state) {
if (outdatedGateways.includes(state?.PreferencesController?.ipfsGateway)) {

View File

@ -25,8 +25,8 @@ export default {
},
}
const hexRegEx = (/^0x[0-9a-f]+$/ui)
const chainIdRegEx = (/^0x[1-9a-f]+[0-9a-f]*$/ui)
const hexRegEx = /^0x[0-9a-f]+$/iu
const chainIdRegEx = /^0x[1-9a-f]+[0-9a-f]*$/iu
function transformState(state = {}) {
// 1. Delete NetworkController.settings
@ -34,12 +34,10 @@ function transformState (state = {}) {
// 2. Migrate NetworkController.provider to Rinkeby or rename rpcTarget key
const provider = state.NetworkController?.provider || {}
const isCustomRpcWithInvalidChainId = (
provider.type === 'rpc' && (
typeof provider.chainId !== 'string' ||
!chainIdRegEx.test(provider.chainId)
)
)
const isCustomRpcWithInvalidChainId =
provider.type === 'rpc' &&
(typeof provider.chainId !== 'string' ||
!chainIdRegEx.test(provider.chainId))
if (isCustomRpcWithInvalidChainId || provider.type === 'localhost') {
state.NetworkController.provider = {
type: 'rinkeby',
@ -84,8 +82,10 @@ function transformState (state = {}) {
typeof metamaskNetworkId === 'string' &&
hexRegEx.test(metamaskNetworkId)
) {
transaction.metamaskNetworkId = parseInt(metamaskNetworkId, 16)
.toString(10)
transaction.metamaskNetworkId = parseInt(
metamaskNetworkId,
16,
).toString(10)
}
})
}
@ -93,7 +93,7 @@ function transformState (state = {}) {
// 6. Convert address book keys from decimal to hex
const addressBook = state.AddressBookController?.addressBook || {}
Object.keys(addressBook).forEach((networkKey) => {
if ((/^\d+$/ui).test(networkKey)) {
if (/^\d+$/iu.test(networkKey)) {
const chainId = `0x${parseInt(networkKey, 10).toString(16)}`
updateChainIds(addressBook[networkKey], chainId)
@ -108,8 +108,7 @@ function transformState (state = {}) {
// 7. Delete localhost key in IncomingTransactionsController
delete state.IncomingTransactionsController
?.incomingTxLastFetchedBlocksByNetwork
?.localhost
?.incomingTxLastFetchedBlocksByNetwork?.localhost
// 8. Merge 'localhost' tokens into 'rpc' tokens
const accountTokens = state.PreferencesController?.accountTokens
@ -121,7 +120,10 @@ function transformState (state = {}) {
const rpcTokens = accountTokens[account].rpc || []
if (rpcTokens.length > 0) {
accountTokens[account].rpc = mergeTokenArrays(localhostTokens, rpcTokens)
accountTokens[account].rpc = mergeTokenArrays(
localhostTokens,
rpcTokens,
)
} else {
accountTokens[account].rpc = localhostTokens
}
@ -155,11 +157,8 @@ function mergeAddressBookKeys (addressBook, networkKey, chainIdKey) {
...Object.keys(networkKeyEntries[address] || {}),
]).forEach((key) => {
// Use non-empty value for the current key, if any
mergedEntry[key] = (
newEntries[address][key] ||
networkKeyEntries[address]?.[key] ||
''
)
mergedEntry[key] =
newEntries[address][key] || networkKeyEntries[address]?.[key] || ''
})
newEntries[address] = mergedEntry

View File

@ -12,7 +12,6 @@ export default function failTxsThat (version, reason, condition) {
console.warn(`MetaMask Migration #${version}${err.stack}`)
}
return Promise.resolve(versionedData)
}
}
@ -38,4 +37,3 @@ function transformState (state, condition, reason) {
}
return newState
}

View File

@ -17,10 +17,14 @@ function start () {
global.platform = new ExtensionPlatform()
const extensionPort = extension.runtime.connect({ name: getEnvironmentType() })
const extensionPort = extension.runtime.connect({
name: getEnvironmentType(),
})
const connectionStream = new PortStream(extensionPort)
const mx = setupMultiplex(connectionStream)
setupControllerConnection(mx.createStream('controller'), (err, metaMaskController) => {
setupControllerConnection(
mx.createStream('controller'),
(err, metaMaskController) => {
if (err) {
return
}
@ -30,7 +34,8 @@ function start () {
metaMaskController.safelistPhishingDomain(suspect.hostname)
window.location.href = suspect.href
})
})
},
)
}
function setupControllerConnection(connectionStream, cb) {
@ -41,5 +46,7 @@ function setupControllerConnection (connectionStream, cb) {
},
})
connectionStream.pipe(metaMaskControllerDnode).pipe(connectionStream)
metaMaskControllerDnode.once('remote', (backgroundConnection) => cb(null, backgroundConnection))
metaMaskControllerDnode.once('remote', (backgroundConnection) =>
cb(null, backgroundConnection),
)
}

View File

@ -4,7 +4,6 @@ import { getEnvironmentType, checkForError } from '../lib/util'
import { ENVIRONMENT_TYPE_BACKGROUND } from '../lib/enums'
export default class ExtensionPlatform {
//
// Public
//
@ -116,7 +115,10 @@ export default class ExtensionPlatform {
if (status === 'confirmed') {
// There was an on-chain failure
receiptStatus === '0x0'
? this._showFailedTransaction(txMeta, 'Transaction encountered an error.')
? this._showFailedTransaction(
txMeta,
'Transaction encountered an error.',
)
: this._showConfirmedTransaction(txMeta)
} else if (status === 'failed') {
this._showFailedTransaction(txMeta)
@ -187,7 +189,6 @@ export default class ExtensionPlatform {
}
_showConfirmedTransaction(txMeta) {
this._subscribeToNotificationClicked()
const url = explorerLink(txMeta.hash, txMeta.metamaskNetworkId)
@ -199,23 +200,21 @@ export default class ExtensionPlatform {
}
_showFailedTransaction(txMeta, errorMessage) {
const nonce = parseInt(txMeta.txParams.nonce, 16)
const title = 'Failed transaction'
const message = `Transaction ${nonce} failed! ${errorMessage || txMeta.err.message}`
const message = `Transaction ${nonce} failed! ${
errorMessage || txMeta.err.message
}`
this._showNotification(title, message)
}
_showNotification(title, message, url) {
extension.notifications.create(
url,
{
'type': 'basic',
extension.notifications.create(url, {
type: 'basic',
title,
'iconUrl': extension.extension.getURL('../../images/icon-64.png'),
iconUrl: extension.extension.getURL('../../images/icon-64.png'),
message,
},
)
})
}
_subscribeToNotificationClicked() {

View File

@ -1,4 +1,3 @@
// this must run before anything else
import './lib/freezeGlobals'
@ -29,7 +28,6 @@ import { getEnvironmentType } from './lib/util'
start().catch(log.error)
async function start() {
// create platform global
global.platform = new ExtensionPlatform()
@ -51,7 +49,8 @@ async function start () {
initializeUiWithTab(activeTab)
function displayCriticalError(container, err) {
container.innerHTML = '<div class="critical-error">The MetaMask app failed to load: please open and close MetaMask again to restart.</div>'
container.innerHTML =
'<div class="critical-error">The MetaMask app failed to load: please open and close MetaMask again to restart.</div>'
container.style.height = '80px'
log.error(err.stack)
throw err
@ -106,11 +105,14 @@ function initializeUi (activeTab, container, connectionStream, cb) {
return
}
launchMetaMaskUi({
launchMetaMaskUi(
{
activeTab,
container,
backgroundConnection,
}, cb)
},
cb,
)
})
}

View File

@ -6,10 +6,7 @@ module.exports = function (api) {
'@babel/preset-env',
{
targets: {
browsers: [
'chrome >= 58',
'firefox >= 56.2',
],
browsers: ['chrome >= 58', 'firefox >= 56.2'],
},
},
],

View File

@ -2,7 +2,10 @@ const fs = require('fs')
const path = require('path')
const { version } = require('../app/manifest/_base.json')
const changelog = fs.readFileSync(path.join(__dirname, '..', 'CHANGELOG.md'), 'utf8')
const changelog = fs.readFileSync(
path.join(__dirname, '..', 'CHANGELOG.md'),
'utf8',
)
const log = changelog.split(version)[1].split('##')[0].trim()
const msg = `*MetaMask ${version}* now published! It should auto-update soon!\n${log}`

View File

@ -49,25 +49,27 @@ function displayChart (data) {
// build bars for bounds
data.forEach((entry, index) => {
const [label, start, end] = entry
const [start2, end2] = [start, end].map((value) => adjust(value, first, last, 40))
const [start2, end2] = [start, end].map((value) =>
adjust(value, first, last, 40),
)
const barString = barBuilder(start2, end2)
const color = colors[index]
const coloredBarString = colorize(color, barString)
const duration = ((end - start) / 1e3).toFixed(1)
console.log(coloredBarString, `${label} ${duration}s`)
})
}
function colorize(color, string) {
const colorizer = (typeof chalk[color] === 'function') ? chalk[color] : chalk.hex(color)
const colorizer =
typeof chalk[color] === 'function' ? chalk[color] : chalk.hex(color)
return colorizer(string)
}
// scale number within bounds
function adjust(value, first, last, size) {
const length = last - first
const result = (value - first) / length * size
const result = ((value - first) / length) * size
return result
}
@ -104,7 +106,7 @@ function getSymbolNormal (value) {
// round to closest supported value
const possibleValues = [0, 1 / 8, 1 / 4, 3 / 8, 1 / 2, 5 / 8, 3 / 4, 7 / 8, 1]
const rounded = possibleValues.reduce((prev, curr) => {
return (Math.abs(curr - value) < Math.abs(prev - value) ? curr : prev)
return Math.abs(curr - value) < Math.abs(prev - value) ? curr : prev
})
if (rounded === 0) {
@ -132,7 +134,7 @@ function getSymbolNormalRight (value) {
// round to closest supported value (not much :/)
const possibleValues = [0, 1 / 2, 7 / 8, 1]
const rounded = possibleValues.reduce((prev, curr) => {
return (Math.abs(curr - value) < Math.abs(prev - value) ? curr : prev)
return Math.abs(curr - value) < Math.abs(prev - value) ? curr : prev
})
if (rounded === 0) {

View File

@ -10,12 +10,13 @@ const { createTask, composeParallel } = require('./task')
module.exports = createEtcTasks
function createEtcTasks({ browserPlatforms, livereload }) {
const clean = createTask('clean', async function clean() {
await del(['./dist/*'])
await Promise.all(browserPlatforms.map(async (platform) => {
await Promise.all(
browserPlatforms.map(async (platform) => {
await fs.mkdir(`./dist/${platform}`, { recursive: true })
}))
}),
)
})
const reload = createTask('reload', function devReload() {
@ -23,9 +24,12 @@ function createEtcTasks ({ browserPlatforms, livereload }) {
})
// zip tasks for distribution
const zip = createTask('zip', composeParallel(
const zip = createTask(
'zip',
composeParallel(
...browserPlatforms.map((platform) => createZipTask(platform)),
))
),
)
return { clean, reload, zip }
}

View File

@ -9,30 +9,32 @@ require('lavamoat-core/lib/ses.umd.js')
lockdown() // eslint-disable-line no-undef
const livereload = require('gulp-livereload')
const { createTask, composeSeries, composeParallel, detectAndRunEntryTask } = require('./task')
const {
createTask,
composeSeries,
composeParallel,
detectAndRunEntryTask,
} = require('./task')
const createManifestTasks = require('./manifest')
const createScriptTasks = require('./scripts')
const createStyleTasks = require('./styles')
const createStaticAssetTasks = require('./static')
const createEtcTasks = require('./etc')
const browserPlatforms = [
'firefox',
'chrome',
'brave',
'opera',
]
const browserPlatforms = ['firefox', 'chrome', 'brave', 'opera']
defineAllTasks()
detectAndRunEntryTask()
function defineAllTasks() {
const staticTasks = createStaticAssetTasks({ livereload, browserPlatforms })
const manifestTasks = createManifestTasks({ browserPlatforms })
const styleTasks = createStyleTasks({ livereload })
const scriptTasks = createScriptTasks({ livereload, browserPlatforms })
const { clean, reload, zip } = createEtcTasks({ livereload, browserPlatforms })
const { clean, reload, zip } = createEtcTasks({
livereload,
browserPlatforms,
})
// build for development (livereload)
createTask(
@ -70,11 +72,7 @@ function defineAllTasks () {
composeSeries(
clean,
styleTasks.prod,
composeParallel(
scriptTasks.prod,
staticTasks.prod,
manifestTasks.prod,
),
composeParallel(scriptTasks.prod, staticTasks.prod, manifestTasks.prod),
zip,
),
)
@ -85,15 +83,10 @@ function defineAllTasks () {
composeSeries(
clean,
styleTasks.prod,
composeParallel(
scriptTasks.test,
staticTasks.prod,
manifestTasks.test,
),
composeParallel(scriptTasks.test, staticTasks.prod, manifestTasks.test),
),
)
// special build for minimal CI testing
createTask('styles', styleTasks.prod)
}

Some files were not shown because too many files have changed in this diff Show More