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