diff options
-rw-r--r-- | app/scripts/metamask-controller.js | 45 | ||||
-rw-r--r-- | docs/porting_to_new_environment.md | 35 |
2 files changed, 65 insertions, 15 deletions
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 393154c60..17d38d43c 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -1391,6 +1391,32 @@ module.exports = class MetamaskController extends EventEmitter { * @param {string} origin - The URI of the requesting resource. */ setupProviderConnection (outStream, origin, publicApi) { + const getSiteMetadata = publicApi && publicApi.getSiteMetadata + const engine = this.setupProviderEngine(origin, getSiteMetadata) + + // setup connection + const providerStream = createEngineStream({ engine }) + + pump( + outStream, + providerStream, + outStream, + (err) => { + // cleanup filter polyfill middleware + engine._middleware.forEach((mid) => { + if (mid.destroy && typeof mid.destroy === 'function') { + mid.destroy() + } + }) + if (err) log.error(err) + } + ) + } + + /** + * A method for creating a provider that is safely restricted for the requesting domain. + **/ + setupProviderEngine (origin, getSiteMetadata) { // setup json rpc engine stack const engine = new RpcEngine() const provider = this.provider @@ -1398,6 +1424,7 @@ module.exports = class MetamaskController extends EventEmitter { // create filter polyfill middleware const filterMiddleware = createFilterMiddleware({ provider, blockTracker }) + // create subscription polyfill middleware const subscriptionManager = createSubscriptionManager({ provider, blockTracker }) subscriptionManager.events.on('notification', (message) => engine.emit('notification', message)) @@ -1413,24 +1440,11 @@ module.exports = class MetamaskController extends EventEmitter { // requestAccounts engine.push(this.providerApprovalController.createMiddleware({ origin, - getSiteMetadata: publicApi && publicApi.getSiteMetadata, + getSiteMetadata, })) // forward to metamask primary provider engine.push(providerAsMiddleware(provider)) - - // setup connection - const providerStream = createEngineStream({ engine }) - - pump( - outStream, - providerStream, - outStream, - (err) => { - // cleanup filter polyfill middleware - filterMiddleware.destroy() - if (err) log.error(err) - } - ) + return engine } /** @@ -1797,3 +1811,4 @@ module.exports = class MetamaskController extends EventEmitter { return this.keyringController.setLocked() } } + diff --git a/docs/porting_to_new_environment.md b/docs/porting_to_new_environment.md index 9c4021781..070ab69fa 100644 --- a/docs/porting_to_new_environment.md +++ b/docs/porting_to_new_environment.md @@ -10,6 +10,41 @@ The `metamask-background` describes the file at `app/scripts/background.js`, whi When a new site is visited, the WebExtension creates a new `ContentScript` in that page's context, which can be seen at `app/scripts/contentscript.js`. This script represents a per-page setup process, which creates the per-page `web3` api, connects it to the background script via the Port API (wrapped in a [stream abstraction](https://github.com/substack/stream-handbook)), and injected into the DOM before anything loads. +You can choose to use this streaming interface to connect to the MetaMask controller over any transport you can wrap with a stream, by connecting it to the [metamask-inpage-provider](https://github.com/MetaMask/metamask-inpage-provider), but you can also construct a provider per domain like this: + +```javascript +const providerFromEngine = require('eth-json-rpc-middleware/providerFromEngine') + +/** +* returns a provider restricted to the requesting domain +**/ +function incomingConnection (domain, getSiteMetadata) { + const engine = metamaskController.setupProviderEngine(domain, getSiteMetadata) + const provider = providerFromEngine(engine) + return provider +} +``` + +Please note if you take this approach, you are responsible for cleaning up the filters when the page is closed: + +``` +const filterMiddleware = engine._middleware.filter(mid => mid.name === 'filterMiddleware')[0] +filterMiddleware.destroy() +``` + +### getSiteMetadata() + +This method is used to enhance our confirmation screens with images and text representing the requesting domain. + +It should return a promise that resolves with an object with the following properties: + +- `name`: The requesting site's name. +- `icon`: A URI representing the site's logo. + +### Using the Streams Interface + +Only use this if you intend to construct the [metamask-inpage-provider](https://github.com/MetaMask/metamask-inpage-provider) over a stream! + The most confusing part about porting MetaMask to a new platform is the way we provide the Web3 API over a series of streams between contexts. Once you understand how we create the [MetamaskInpageProvider](https://github.com/MetaMask/metamask-inpage-provider/blob/master/index.js) in the [inpage.js script](../app/scripts/inpage.js), you will be able to understand how the [port-stream](../app/scripts/lib/port-stream.js) is just a thin wrapper around the [postMessage API](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage), and a similar stream API can be wrapped around any communication channel to communicate with the `MetaMaskController` via its `setupUntrustedCommunication(stream, domain)` method. ### The MetaMask Controller |