diff --git a/CHANGELOG.md b/CHANGELOG.md index 88c299c7d..e3ab9d3b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## Current Develop Branch +## 9.0.1 Wed Jan 13 2021 +- [#10169](https://github.com/MetaMask/metamask-extension/pull/10169): Improved detection of contract methods with array parameters +- [#10178](https://github.com/MetaMask/metamask-extension/pull/10178): Only warn of injected web3 usage once per page +- [#10179](https://github.com/MetaMask/metamask-extension/pull/10179): Restore support for @metamask/inpage provider@"< 8.0.0" +- [#10180](https://github.com/MetaMask/metamask-extension/pull/10180): Fix UI crash when domain metadata is missing on public encryption key confirmation page + ## 9.0.0 Fri Jan 8 2021 - [#9156](https://github.com/MetaMask/metamask-extension/pull/9156): Remove window.web3 injection diff --git a/app/manifest/_base.json b/app/manifest/_base.json index 5fef12ea1..6b6a4e2b0 100644 --- a/app/manifest/_base.json +++ b/app/manifest/_base.json @@ -78,6 +78,6 @@ "notifications" ], "short_name": "__MSG_appName__", - "version": "9.0.0", + "version": "9.0.1", "web_accessible_resources": ["inpage.js", "phishing.html"] } diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js index 7e55faec4..116cad02d 100644 --- a/app/scripts/contentscript.js +++ b/app/scripts/contentscript.js @@ -4,6 +4,7 @@ import LocalMessageDuplexStream from 'post-message-stream' import ObjectMultiplex from 'obj-multiplex' import extension from 'extensionizer' import PortStream from 'extension-port-stream' +import { obj as createThoughStream } from 'through2' // These require calls need to use require to be statically recognized by browserify const fs = require('fs') @@ -20,6 +21,12 @@ const CONTENT_SCRIPT = 'metamask-contentscript' const INPAGE = 'metamask-inpage' const PROVIDER = 'metamask-provider' +// TODO:LegacyProvider: Delete +const LEGACY_CONTENT_SCRIPT = 'contentscript' +const LEGACY_INPAGE = 'inpage' +const LEGACY_PROVIDER = 'provider' +const LEGACY_PUBLIC_CONFIG = 'publicConfig' + if (shouldInjectProvider()) { injectScript(inpageBundle) setupStreams() @@ -63,6 +70,7 @@ async function setupStreams() { pageMux.setMaxListeners(25) const extensionMux = new ObjectMultiplex() extensionMux.setMaxListeners(25) + extensionMux.ignoreStream(LEGACY_PUBLIC_CONFIG) // TODO:LegacyProvider: Delete pump(pageMux, pageStream, pageMux, (err) => logStreamDisconnectWarning('MetaMask Inpage Multiplex', err), @@ -78,6 +86,44 @@ async function setupStreams() { // connect "phishing" channel to warning system const phishingStream = extensionMux.createStream('phishing') phishingStream.once('data', redirectToPhishingWarning) + + // TODO:LegacyProvider: Delete + // handle legacy provider + const legacyPageStream = new LocalMessageDuplexStream({ + name: LEGACY_CONTENT_SCRIPT, + target: LEGACY_INPAGE, + }) + + const legacyPageMux = new ObjectMultiplex() + legacyPageMux.setMaxListeners(25) + const legacyExtensionMux = new ObjectMultiplex() + legacyExtensionMux.setMaxListeners(25) + + pump(legacyPageMux, legacyPageStream, legacyPageMux, (err) => + logStreamDisconnectWarning('MetaMask Legacy Inpage Multiplex', err), + ) + pump( + legacyExtensionMux, + extensionStream, + getNotificationTransformStream(), + legacyExtensionMux, + (err) => { + logStreamDisconnectWarning('MetaMask Background Legacy Multiplex', err) + notifyInpageOfStreamFailure() + }, + ) + + forwardNamedTrafficBetweenMuxes( + LEGACY_PROVIDER, + PROVIDER, + legacyPageMux, + legacyExtensionMux, + ) + forwardTrafficBetweenMuxes( + LEGACY_PUBLIC_CONFIG, + legacyPageMux, + legacyExtensionMux, + ) } function forwardTrafficBetweenMuxes(channelName, muxA, muxB) { @@ -91,6 +137,37 @@ function forwardTrafficBetweenMuxes(channelName, muxA, muxB) { ) } +// TODO:LegacyProvider: Delete +function forwardNamedTrafficBetweenMuxes( + channelAName, + channelBName, + muxA, + muxB, +) { + const channelA = muxA.createStream(channelAName) + const channelB = muxB.createStream(channelBName) + pump(channelA, channelB, channelA, (error) => + console.debug( + `MetaMask: Muxed traffic between channels "${channelAName}" and "${channelBName}" failed.`, + error, + ), + ) +} + +// TODO:LegacyProvider: Delete +function getNotificationTransformStream() { + return createThoughStream((chunk, _, cb) => { + if (chunk?.name === PROVIDER) { + if (chunk.data?.method === 'metamask_accountsChanged') { + chunk.data.method = 'wallet_accountsChanged' + chunk.data.result = chunk.data.params + delete chunk.data.params + } + } + cb(null, chunk) + }) +} + /** * Error handler for page to extension stream disconnections * diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 0608ac2b8..2e76464aa 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -1,6 +1,8 @@ import EventEmitter from 'events' import pump from 'pump' import Dnode from 'dnode' +import { ObservableStore } from '@metamask/obs-store' +import { storeAsStream } from '@metamask/obs-store/dist/asStream' import { JsonRpcEngine } from 'json-rpc-engine' import { debounce } from 'lodash' import createEngineStream from 'json-rpc-middleware-stream/engineStream' @@ -413,6 +415,9 @@ export default class MetamaskController extends EventEmitter { ) { this.submitPassword(password) } + + // TODO:LegacyProvider: Delete + this.publicConfigStore = this.createPublicConfigStore() } /** @@ -459,6 +464,38 @@ export default class MetamaskController extends EventEmitter { return providerProxy } + /** + * TODO:LegacyProvider: Delete + * Constructor helper: initialize a public config store. + * This store is used to make some config info available to Dapps synchronously. + */ + createPublicConfigStore() { + // subset of state for metamask inpage provider + const publicConfigStore = new ObservableStore() + const { networkController } = this + + // setup memStore subscription hooks + this.on('update', updatePublicConfigStore) + updatePublicConfigStore(this.getState()) + + function updatePublicConfigStore(memState) { + const chainId = networkController.getCurrentChainId() + if (memState.network !== 'loading') { + publicConfigStore.putState(selectPublicState(chainId, memState)) + } + } + + function selectPublicState(chainId, { isUnlocked, network }) { + return { + isUnlocked, + chainId, + networkVersion: network, + } + } + + return publicConfigStore + } + /** * Gets relevant state for the provider of an external origin. * @@ -1831,6 +1868,10 @@ export default class MetamaskController extends EventEmitter { // messages between inpage and background this.setupProviderConnection(mux.createStream('metamask-provider'), sender) + + // TODO:LegacyProvider: Delete + // legacy streams + this.setupPublicConfig(mux.createStream('publicConfig')) } /** @@ -2016,6 +2057,28 @@ export default class MetamaskController extends EventEmitter { return engine } + /** + * TODO:LegacyProvider: Delete + * A method for providing our public config info over a stream. + * This includes info we like to be synchronous if possible, like + * the current selected account, and network ID. + * + * Since synchronous methods have been deprecated in web3, + * this is a good candidate for deprecation. + * + * @param {*} outStream - The stream to provide public config over. + */ + setupPublicConfig(outStream) { + const configStream = storeAsStream(this.publicConfigStore) + + pump(configStream, outStream, (err) => { + configStream.destroy() + if (err) { + log.error(err) + } + }) + } + /** * Adds a reference to a connection by origin. Ignores the 'metamask' origin. * Caller must ensure that the returned id is stored such that the reference diff --git a/package.json b/package.json index 312789a00..ba59f09d3 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,7 @@ "@metamask/eth-ledger-bridge-keyring": "^0.2.6", "@metamask/eth-token-tracker": "^3.0.1", "@metamask/etherscan-link": "^1.4.0", - "@metamask/inpage-provider": "^8.0.1", + "@metamask/inpage-provider": "^8.0.2", "@metamask/jazzicon": "^2.0.0", "@metamask/logo": "^2.5.0", "@metamask/obs-store": "^5.0.0", @@ -110,7 +110,7 @@ "eth-json-rpc-infura": "^5.1.0", "eth-json-rpc-middleware": "^6.0.0", "eth-keyring-controller": "^6.1.0", - "eth-method-registry": "^1.2.0", + "eth-method-registry": "^2.0.0", "eth-phishing-detect": "^1.1.14", "eth-query": "^2.1.2", "eth-sig-util": "^3.0.0", diff --git a/ui/app/helpers/utils/transactions.util.js b/ui/app/helpers/utils/transactions.util.js index 6dda958cc..bcafae970 100644 --- a/ui/app/helpers/utils/transactions.util.js +++ b/ui/app/helpers/utils/transactions.util.js @@ -1,4 +1,4 @@ -import MethodRegistry from 'eth-method-registry' +import { MethodRegistry } from 'eth-method-registry' import abi from 'human-standard-token-abi' import { ethers } from 'ethers' import log from 'loglevel' diff --git a/ui/app/pages/confirm-encryption-public-key/confirm-encryption-public-key.component.js b/ui/app/pages/confirm-encryption-public-key/confirm-encryption-public-key.component.js index 08a3cbf95..c3d1dfaa2 100644 --- a/ui/app/pages/confirm-encryption-public-key/confirm-encryption-public-key.component.js +++ b/ui/app/pages/confirm-encryption-public-key/confirm-encryption-public-key.component.js @@ -158,23 +158,24 @@ export default class ConfirmEncryptionPublicKey extends Component { const { domainMetadata, txData } = this.props const { t } = this.context - const origin = domainMetadata[txData.origin] - const notice = t('encryptionPublicKeyNotice', [origin.name]) + const originMetadata = domainMetadata[txData.origin] + const notice = t('encryptionPublicKeyNotice', [txData.origin]) + const name = originMetadata?.name || txData.origin return (