mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 01:47:00 +01:00
Abstract domain provider from its stream transport (#6670)
* Abstract domain provider from its stream transport Creating new provider-consuming extensions, like [a new platform](https://github.com/MetaMask/metamask-extension/blob/develop/docs/porting_to_new_environment.md) can be frustrating for new contributors because our provider construction has been tangled with our streaming interface. Here I've broken up our streaming domain connection from the provider construction, so developers can more easily construct local and domain-restricted providers without dealing with streams. * Abstract public API from stream interface * clean up noop * Document non-streaming interface * getSiteMetadata must be async * Clean up filters on stream end * Document cleaning up filters * Allow named filterMiddleware to be cleaned up * Linted * Require site metadata * Destroy any destroyable middleware during cleanup * Lint
This commit is contained in:
parent
32fffe53f5
commit
78cdee23c0
@ -1391,6 +1391,32 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
* @param {string} origin - The URI of the requesting resource.
|
* @param {string} origin - The URI of the requesting resource.
|
||||||
*/
|
*/
|
||||||
setupProviderConnection (outStream, origin, publicApi) {
|
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
|
// setup json rpc engine stack
|
||||||
const engine = new RpcEngine()
|
const engine = new RpcEngine()
|
||||||
const provider = this.provider
|
const provider = this.provider
|
||||||
@ -1398,6 +1424,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
|
|
||||||
// create filter polyfill middleware
|
// create filter polyfill middleware
|
||||||
const filterMiddleware = createFilterMiddleware({ provider, blockTracker })
|
const filterMiddleware = createFilterMiddleware({ provider, blockTracker })
|
||||||
|
|
||||||
// create subscription polyfill middleware
|
// create subscription polyfill middleware
|
||||||
const subscriptionManager = createSubscriptionManager({ provider, blockTracker })
|
const subscriptionManager = createSubscriptionManager({ provider, blockTracker })
|
||||||
subscriptionManager.events.on('notification', (message) => engine.emit('notification', message))
|
subscriptionManager.events.on('notification', (message) => engine.emit('notification', message))
|
||||||
@ -1413,24 +1440,11 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
// requestAccounts
|
// requestAccounts
|
||||||
engine.push(this.providerApprovalController.createMiddleware({
|
engine.push(this.providerApprovalController.createMiddleware({
|
||||||
origin,
|
origin,
|
||||||
getSiteMetadata: publicApi && publicApi.getSiteMetadata,
|
getSiteMetadata,
|
||||||
}))
|
}))
|
||||||
// forward to metamask primary provider
|
// forward to metamask primary provider
|
||||||
engine.push(providerAsMiddleware(provider))
|
engine.push(providerAsMiddleware(provider))
|
||||||
|
return engine
|
||||||
// setup connection
|
|
||||||
const providerStream = createEngineStream({ engine })
|
|
||||||
|
|
||||||
pump(
|
|
||||||
outStream,
|
|
||||||
providerStream,
|
|
||||||
outStream,
|
|
||||||
(err) => {
|
|
||||||
// cleanup filter polyfill middleware
|
|
||||||
filterMiddleware.destroy()
|
|
||||||
if (err) log.error(err)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1797,3 +1811,4 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
return this.keyringController.setLocked()
|
return this.keyringController.setLocked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.
|
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 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
|
### The MetaMask Controller
|
||||||
|
Loading…
Reference in New Issue
Block a user