diff --git a/app/scripts/inpage.js b/app/scripts/inpage.js index 8982a93f26a5..801539376a75 100644 --- a/app/scripts/inpage.js +++ b/app/scripts/inpage.js @@ -35,10 +35,6 @@ import log from 'loglevel' import LocalMessageDuplexStream from 'post-message-stream' import { initProvider } from '@metamask/inpage-provider' -// TODO:deprecate:2020 -import setupWeb3 from './lib/setupWeb3' -/* eslint-enable import/first */ - restoreContextAfterImports() log.setDefaultLevel(process.env.METAMASK_DEBUG ? 'debug' : 'warn') @@ -56,17 +52,3 @@ const metamaskStream = new LocalMessageDuplexStream({ initProvider({ connectionStream: metamaskStream, }) - -// TODO:deprecate:2020 -// Setup web3 - -if (typeof window.web3 === 'undefined') { - // proxy web3, assign to window, and set up site auto reload - setupWeb3(log) -} else { - log.warn(`MetaMask detected another web3. - MetaMask will not work reliably with another web3 extension. - This usually happens if you have two MetaMasks installed, - or MetaMask and another web3 extension. Please remove one - and try again.`) -} diff --git a/app/scripts/lib/enums.js b/app/scripts/lib/enums.js index d93030f2c48c..22b3488bc99b 100644 --- a/app/scripts/lib/enums.js +++ b/app/scripts/lib/enums.js @@ -23,7 +23,7 @@ const MESSAGE_TYPE = { ETH_GET_ENCRYPTION_PUBLIC_KEY: 'eth_getEncryptionPublicKey', ETH_SIGN: 'eth_sign', ETH_SIGN_TYPED_DATA: 'eth_signTypedData', - LOG_WEB3_USAGE: 'metamask_logInjectedWeb3Usage', + LOG_WEB3_SHIM_USAGE: 'metamask_logWeb3ShimUsage', PERSONAL_SIGN: 'personal_sign', WATCH_ASSET: 'wallet_watchAsset', WATCH_ASSET_LEGACY: 'metamask_watchAsset', diff --git a/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js b/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js index b87047cd2494..fdbc265e9e49 100644 --- a/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js +++ b/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js @@ -21,7 +21,6 @@ const handlerMap = handlers.reduce((map, handler) => { * Eventually, we'll want to extract this middleware into its own package. * * @param {Object} opts - The middleware options - * @param {string} opts.origin - The origin for the middleware stack * @param {Function} opts.sendMetrics - A function for sending a metrics event * @returns {(req: Object, res: Object, next: Function, end: Function) => void} */ diff --git a/app/scripts/lib/rpc-method-middleware/handlers/index.js b/app/scripts/lib/rpc-method-middleware/handlers/index.js index 74e26b675f90..e19278a7d777 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/index.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/index.js @@ -1,5 +1,5 @@ -import logWeb3Usage from './log-web3-usage' +import logWeb3ShimUsage from './log-web3-shim-usage' import watchAsset from './watch-asset' -const handlers = [logWeb3Usage, watchAsset] +const handlers = [logWeb3ShimUsage, watchAsset] export default handlers diff --git a/app/scripts/lib/rpc-method-middleware/handlers/log-web3-shim-usage.js b/app/scripts/lib/rpc-method-middleware/handlers/log-web3-shim-usage.js new file mode 100644 index 000000000000..142e46f446dd --- /dev/null +++ b/app/scripts/lib/rpc-method-middleware/handlers/log-web3-shim-usage.js @@ -0,0 +1,49 @@ +import { MESSAGE_TYPE } from '../../enums' + +/** + * This RPC method is called by the inpage provider whenever it detects the + * accessing of a non-existent property on our window.web3 shim. + * We collect this data to understand which sites are breaking due to the + * removal of our window.web3. + */ + +const logWeb3ShimUsage = { + methodNames: [MESSAGE_TYPE.LOG_WEB3_SHIM_USAGE], + implementation: logWeb3ShimUsageHandler, +} +export default logWeb3ShimUsage + +const recordedWeb3ShimUsage = {} + +/** + * @typedef {Object} LogWeb3ShimUsageOptions + * @property {Function} sendMetrics - A function that registers a metrics event. + */ + +/** + * @param {import('json-rpc-engine').JsonRpcRequest} req - The JSON-RPC request object. + * @param {import('json-rpc-engine').JsonRpcResponse} res - The JSON-RPC response object. + * @param {Function} _next - The json-rpc-engine 'next' callback. + * @param {Function} end - The json-rpc-engine 'end' callback. + * @param {LogWeb3ShimUsageOptions} options + */ +function logWeb3ShimUsageHandler(req, res, _next, end, { sendMetrics }) { + const { origin } = req + if (!recordedWeb3ShimUsage[origin]) { + recordedWeb3ShimUsage[origin] = true + + sendMetrics({ + event: `Website Accessed window.web3 Shim`, + category: 'inpage_provider', + eventContext: { + referrer: { + url: origin, + }, + }, + excludeMetaMetricsId: true, + }) + } + + res.result = true + return end() +} diff --git a/app/scripts/lib/rpc-method-middleware/handlers/log-web3-usage.js b/app/scripts/lib/rpc-method-middleware/handlers/log-web3-usage.js deleted file mode 100644 index c80303223422..000000000000 --- a/app/scripts/lib/rpc-method-middleware/handlers/log-web3-usage.js +++ /dev/null @@ -1,63 +0,0 @@ -import { MESSAGE_TYPE } from '../../enums' - -/** - * This RPC method is called by our inpage web3 proxy whenever window.web3 is - * accessed. We're collecting data on window.web3 usage so that we can warn - * website maintainers, and possibly our users, before we remove window.web3 - * by November 16, 2020. - */ - -const logWeb3Usage = { - methodNames: [MESSAGE_TYPE.LOG_WEB3_USAGE], - implementation: logWeb3UsageHandler, -} -export default logWeb3Usage - -const recordedWeb3Usage = {} - -/** - * @typedef {Object} LogWeb3UsageOptions - * @property {string} origin - The origin of the request. - * @property {Function} sendMetrics - A function that registers a metrics event. - */ - -/** - * @typedef {Object} LogWeb3UsageParam - * @property {string} action - The action taken (get or set). - * @property {string} name - The window.web3 property name subject to the action. - */ - -/** - * @param {import('json-rpc-engine').JsonRpcRequest<[LogWeb3UsageParam]>} req - The JSON-RPC request object. - * @param {import('json-rpc-engine').JsonRpcResponse} res - The JSON-RPC response object. - * @param {Function} _next - The json-rpc-engine 'next' callback. - * @param {Function} end - The json-rpc-engine 'end' callback. - * @param {LogWeb3UsageOptions} options - */ -function logWeb3UsageHandler(req, res, _next, end, { origin, sendMetrics }) { - const { action, path } = req.params[0] - - if (!recordedWeb3Usage[origin]) { - recordedWeb3Usage[origin] = {} - } - if (!recordedWeb3Usage[origin][path]) { - recordedWeb3Usage[origin][path] = true - - sendMetrics( - { - event: `Website Used window.web3`, - category: 'inpage_provider', - properties: { action, web3Path: path }, - referrer: { - url: origin, - }, - }, - { - excludeMetaMetricsId: true, - }, - ) - } - - res.result = true - return end() -} diff --git a/app/scripts/lib/setupWeb3.js b/app/scripts/lib/setupWeb3.js deleted file mode 100644 index 9dc74ddb2a6e..000000000000 --- a/app/scripts/lib/setupWeb3.js +++ /dev/null @@ -1,251 +0,0 @@ -/*global Web3*/ - -// TODO:deprecate:2020 -// Delete this file - -import web3Entitites from './web3-entities.json' -import 'web3/dist/web3.min' - -const shouldLogUsage = ![ - 'docs.metamask.io', - 'metamask.github.io', - 'metamask.io', -].includes(window.location.hostname) - -/** - * To understand how we arrived at this implementation, please see: - * https://github.com/ethereum/web3.js/blob/0.20.7/DOCUMENTATION.md - */ -export default function setupWeb3(log) { - // export web3 as a global, checking for usage - let reloadInProgress = false - let lastTimeUsed - let lastSeenNetwork - let hasBeenWarned = false - - const web3 = new Web3(window.ethereum) - web3.setProvider = function () { - log.debug('MetaMask - overrode web3.setProvider') - } - Object.defineProperty(web3, '__isMetaMaskShim__', { - value: true, - enumerable: false, - configurable: false, - writable: false, - }) - - Object.defineProperty(window.ethereum, '_web3Ref', { - enumerable: false, - writable: true, - configurable: true, - value: web3.eth, - }) - - // Setup logging of nested property usage - if (shouldLogUsage) { - // web3 namespaces with common and uncommon dapp actions - const includedTopKeys = [ - 'eth', - 'db', - 'shh', - 'net', - 'personal', - 'bzz', - 'version', - ] - - // For each top-level property, create appropriate Proxy traps for all of - // their properties - includedTopKeys.forEach((topKey) => { - const applyTrapKeys = new Map() - const getTrapKeys = new Map() - - Object.keys(web3[topKey]).forEach((key) => { - const path = `web3.${topKey}.${key}` - - if (web3Entitites[path]) { - if (web3Entitites[path] === 'function') { - applyTrapKeys.set(key, path) - } else { - getTrapKeys.set(key, path) - } - } - }) - - // Create apply traps for function properties - for (const [key, path] of applyTrapKeys) { - web3[topKey][key] = new Proxy(web3[topKey][key], { - apply: (...params) => { - try { - window.ethereum.request({ - method: 'metamask_logInjectedWeb3Usage', - params: [ - { - action: 'apply', - path, - }, - ], - }) - } catch (error) { - log.debug('Failed to log web3 usage.', error) - } - - // Call function normally - return Reflect.apply(...params) - }, - }) - } - - // Create get trap for non-function properties - web3[topKey] = new Proxy(web3[topKey], { - get: (web3Prop, key, ...params) => { - const name = stringifyKey(key) - - if (getTrapKeys.has(name)) { - try { - window.ethereum.request({ - method: 'metamask_logInjectedWeb3Usage', - params: [ - { - action: 'get', - path: getTrapKeys.get(name), - }, - ], - }) - } catch (error) { - log.debug('Failed to log web3 usage.', error) - } - } - - // return value normally - return Reflect.get(web3Prop, key, ...params) - }, - }) - }) - - const topLevelFunctions = [ - 'isConnected', - 'setProvider', - 'reset', - 'sha3', - 'toHex', - 'toAscii', - 'fromAscii', - 'toDecimal', - 'fromDecimal', - 'fromWei', - 'toWei', - 'toBigNumber', - 'isAddress', - ] - - // apply-trap top-level functions - topLevelFunctions.forEach((key) => { - // This type check is probably redundant, but we've been burned before. - if (typeof web3[key] === 'function') { - web3[key] = new Proxy(web3[key], { - apply: (...params) => { - try { - window.ethereum.request({ - method: 'metamask_logInjectedWeb3Usage', - params: [ - { - action: 'apply', - path: `web3.${key}`, - }, - ], - }) - } catch (error) { - log.debug('Failed to log web3 usage.', error) - } - - // Call function normally - return Reflect.apply(...params) - }, - }) - } - }) - } - - const web3Proxy = new Proxy(web3, { - get: (...params) => { - // 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`, - ) - hasBeenWarned = true - } - - // return value normally - return Reflect.get(...params) - }, - }) - - Object.defineProperty(window, 'web3', { - enumerable: false, - writable: true, - configurable: true, - value: web3Proxy, - }) - log.debug('MetaMask - injected web3') - - window.ethereum._publicConfigStore.subscribe((state) => { - // if the auto refresh on network change is false do not - // do anything - if (!window.ethereum.autoRefreshOnNetworkChange) { - return - } - - // if reload in progress, no need to check reload logic - if (reloadInProgress) { - return - } - - const currentNetwork = state.networkVersion - - // set the initial network - if (!lastSeenNetwork) { - lastSeenNetwork = currentNetwork - return - } - - // skip reload logic if web3 not used - if (!lastTimeUsed) { - return - } - - // if network did not change, exit - if (currentNetwork === lastSeenNetwork) { - return - } - - // initiate page reload - reloadInProgress = true - const timeSinceUse = Date.now() - lastTimeUsed - // if web3 was recently used then delay the reloading of the page - if (timeSinceUse > 500) { - triggerReset() - } else { - setTimeout(triggerReset, 500) - } - }) -} - -// reload the page -function triggerReset() { - window.location.reload() -} - -/** - * Returns a "stringified" key. Keys that are already strings are returned - * unchanged, and any non-string values are returned as "typeof ". - * - * @param {any} key - The key to stringify - */ -function stringifyKey(key) { - return typeof key === 'string' ? key : `typeof ${typeof key}` -} diff --git a/app/scripts/lib/web3-entities.json b/app/scripts/lib/web3-entities.json deleted file mode 100644 index 0e9b435bc315..000000000000 --- a/app/scripts/lib/web3-entities.json +++ /dev/null @@ -1,101 +0,0 @@ -{ - "web3.bzz.blockNetworkRead": "function", - "web3.bzz.download": "function", - "web3.bzz.get": "function", - "web3.bzz.getHive": "function", - "web3.bzz.getInfo": "function", - "web3.bzz.hive": "TRAP", - "web3.bzz.info": "TRAP", - "web3.bzz.modify": "function", - "web3.bzz.put": "function", - "web3.bzz.retrieve": "function", - "web3.bzz.store": "function", - "web3.bzz.swapEnabled": "function", - "web3.bzz.syncEnabled": "function", - "web3.bzz.upload": "function", - "web3.db.getHex": "function", - "web3.db.getString": "function", - "web3.db.putHex": "function", - "web3.db.putString": "function", - "web3.eth.accounts": "object", - "web3.eth.blockNumber": "TRAP", - "web3.eth.call": "function", - "web3.eth.coinbase": "object", - "web3.eth.compile": "object", - "web3.eth.estimateGas": "function", - "web3.eth.gasPrice": "TRAP", - "web3.eth.getAccounts": "function", - "web3.eth.getBalance": "function", - "web3.eth.getBlock": "function", - "web3.eth.getBlockNumber": "function", - "web3.eth.getBlockTransactionCount": "function", - "web3.eth.getBlockUncleCount": "function", - "web3.eth.getCode": "function", - "web3.eth.getCoinbase": "function", - "web3.eth.getCompilers": "function", - "web3.eth.getGasPrice": "function", - "web3.eth.getHashrate": "function", - "web3.eth.getMining": "function", - "web3.eth.getProtocolVersion": "function", - "web3.eth.getStorageAt": "function", - "web3.eth.getSyncing": "function", - "web3.eth.getTransaction": "function", - "web3.eth.getTransactionCount": "function", - "web3.eth.getTransactionFromBlock": "function", - "web3.eth.getTransactionReceipt": "function", - "web3.eth.getUncle": "function", - "web3.eth.getWork": "function", - "web3.eth.hashrate": "TRAP", - "web3.eth.iban": "function", - "web3.eth.mining": "TRAP", - "web3.eth.protocolVersion": "TRAP", - "web3.eth.sendIBANTransaction": "function", - "web3.eth.sendRawTransaction": "function", - "web3.eth.sendTransaction": "function", - "web3.eth.sign": "function", - "web3.eth.signTransaction": "function", - "web3.eth.submitWork": "function", - "web3.eth.syncing": "TRAP", - "web3.net.getListening": "function", - "web3.net.getPeerCount": "function", - "web3.net.listening": "TRAP", - "web3.net.peerCount": "TRAP", - "web3.personal.ecRecover": "function", - "web3.personal.getListAccounts": "function", - "web3.personal.importRawKey": "function", - "web3.personal.listAccounts": "TRAP", - "web3.personal.lockAccount": "function", - "web3.personal.newAccount": "function", - "web3.personal.sendTransaction": "function", - "web3.personal.sign": "function", - "web3.personal.unlockAccount": "function", - "web3.providers.HttpProvider": "function", - "web3.providers.IpcProvider": "function", - "web3.shh.addPrivateKey": "function", - "web3.shh.addSymKey": "function", - "web3.shh.deleteKeyPair": "function", - "web3.shh.deleteSymKey": "function", - "web3.shh.generateSymKeyFromPassword": "function", - "web3.shh.getPrivateKey": "function", - "web3.shh.getPublicKey": "function", - "web3.shh.getSymKey": "function", - "web3.shh.hasKeyPair": "function", - "web3.shh.hasSymKey": "function", - "web3.shh.info": "function", - "web3.shh.markTrustedPeer": "function", - "web3.shh.newKeyPair": "function", - "web3.shh.newSymKey": "function", - "web3.shh.post": "function", - "web3.shh.setMaxMessageSize": "function", - "web3.shh.setMinPoW": "function", - "web3.shh.version": "function", - "web3.version.api": "string", - "web3.version.ethereum": "TRAP", - "web3.version.getEthereum": "function", - "web3.version.getNetwork": "function", - "web3.version.getNode": "function", - "web3.version.getWhisper": "function", - "web3.version.network": "string", - "web3.version.node": "TRAP", - "web3.version.whisper": "TRAP" -}