1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00

Remove web3 injection (#9156)

* Remove web3 injection
* Implement logWeb3ShimUsage
This commit is contained in:
Erik Marks 2020-12-07 12:09:02 -08:00 committed by GitHub
parent 26272d3557
commit 6795298c65
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 52 additions and 437 deletions

View File

@ -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.`)
}

View File

@ -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',

View File

@ -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}
*/

View File

@ -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

View File

@ -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<unknown>} req - The JSON-RPC request object.
* @param {import('json-rpc-engine').JsonRpcResponse<true>} 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()
}

View File

@ -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<true>} 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()
}

View File

@ -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 <type>".
*
* @param {any} key - The key to stringify
*/
function stringifyKey(key) {
return typeof key === 'string' ? key : `typeof ${typeof key}`
}

View File

@ -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"
}