mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 01:47:00 +01:00
Refactor ProviderApprovalController to use rpc and publicConfigStore (#6410)
* Ensure home screen does not render if there are unapproved txs (#6501) * Ensure that the confirm screen renders before the home screen if there are unapproved txs. * Only render confirm screen before home screen on mount. * inpage - revert _metamask api to isEnabled isApproved isUnlocked
This commit is contained in:
parent
2ff522604b
commit
2845398c3d
@ -1,18 +1,17 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const pump = require('pump')
|
||||
const log = require('loglevel')
|
||||
const Dnode = require('dnode')
|
||||
const querystring = require('querystring')
|
||||
const LocalMessageDuplexStream = require('post-message-stream')
|
||||
const PongStream = require('ping-pong-stream/pong')
|
||||
const ObjectMultiplex = require('obj-multiplex')
|
||||
const extension = require('extensionizer')
|
||||
const PortStream = require('extension-port-stream')
|
||||
const {Transform: TransformStream} = require('stream')
|
||||
|
||||
const inpageContent = fs.readFileSync(path.join(__dirname, '..', '..', 'dist', 'chrome', 'inpage.js')).toString()
|
||||
const inpageSuffix = '//# sourceURL=' + extension.extension.getURL('inpage.js') + '\n'
|
||||
const inpageBundle = inpageContent + inpageSuffix
|
||||
let isEnabled = false
|
||||
|
||||
// Eventually this streaming injection could be replaced with:
|
||||
// https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Language_Bindings/Components.utils.exportFunction
|
||||
@ -23,9 +22,7 @@ let isEnabled = false
|
||||
|
||||
if (shouldInjectWeb3()) {
|
||||
injectScript(inpageBundle)
|
||||
setupStreams()
|
||||
listenForProviderRequest()
|
||||
checkPrivacyMode()
|
||||
start()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -47,148 +44,107 @@ function injectScript (content) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up two-way communication streams between the
|
||||
* browser extension and local per-page browser context
|
||||
* Sets up the stream communication and submits site metadata
|
||||
*
|
||||
*/
|
||||
function setupStreams () {
|
||||
// setup communication to page and plugin
|
||||
async function start () {
|
||||
await setupStreams()
|
||||
await domIsReady()
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up two-way communication streams between the
|
||||
* browser extension and local per-page browser context.
|
||||
*
|
||||
*/
|
||||
async function setupStreams () {
|
||||
// the transport-specific streams for communication between inpage and background
|
||||
const pageStream = new LocalMessageDuplexStream({
|
||||
name: 'contentscript',
|
||||
target: 'inpage',
|
||||
})
|
||||
const pluginPort = extension.runtime.connect({ name: 'contentscript' })
|
||||
const pluginStream = new PortStream(pluginPort)
|
||||
const extensionPort = extension.runtime.connect({ name: 'contentscript' })
|
||||
const extensionStream = new PortStream(extensionPort)
|
||||
|
||||
// Filter out selectedAddress until this origin is enabled
|
||||
const approvalTransform = new TransformStream({
|
||||
objectMode: true,
|
||||
transform: (data, _, done) => {
|
||||
if (typeof data === 'object' && data.name && data.name === 'publicConfig' && !isEnabled) {
|
||||
data.data.selectedAddress = undefined
|
||||
}
|
||||
done(null, { ...data })
|
||||
},
|
||||
})
|
||||
// create and connect channel muxers
|
||||
// so we can handle the channels individually
|
||||
const pageMux = new ObjectMultiplex()
|
||||
pageMux.setMaxListeners(25)
|
||||
const extensionMux = new ObjectMultiplex()
|
||||
extensionMux.setMaxListeners(25)
|
||||
|
||||
// forward communication plugin->inpage
|
||||
pump(
|
||||
pageMux,
|
||||
pageStream,
|
||||
pluginStream,
|
||||
approvalTransform,
|
||||
pageStream,
|
||||
(err) => logStreamDisconnectWarning('MetaMask Contentscript Forwarding', err)
|
||||
)
|
||||
|
||||
// setup local multistream channels
|
||||
const mux = new ObjectMultiplex()
|
||||
mux.setMaxListeners(25)
|
||||
|
||||
pump(
|
||||
mux,
|
||||
pageStream,
|
||||
mux,
|
||||
(err) => logStreamDisconnectWarning('MetaMask Inpage', err)
|
||||
pageMux,
|
||||
(err) => logStreamDisconnectWarning('MetaMask Inpage Multiplex', err)
|
||||
)
|
||||
pump(
|
||||
mux,
|
||||
pluginStream,
|
||||
mux,
|
||||
(err) => logStreamDisconnectWarning('MetaMask Background', err)
|
||||
extensionMux,
|
||||
extensionStream,
|
||||
extensionMux,
|
||||
(err) => logStreamDisconnectWarning('MetaMask Background Multiplex', err)
|
||||
)
|
||||
|
||||
// connect ping stream
|
||||
const pongStream = new PongStream({ objectMode: true })
|
||||
pump(
|
||||
mux,
|
||||
pongStream,
|
||||
mux,
|
||||
(err) => logStreamDisconnectWarning('MetaMask PingPongStream', err)
|
||||
)
|
||||
// forward communication across inpage-background for these channels only
|
||||
forwardTrafficBetweenMuxers('provider', pageMux, extensionMux)
|
||||
forwardTrafficBetweenMuxers('publicConfig', pageMux, extensionMux)
|
||||
|
||||
// connect phishing warning stream
|
||||
const phishingStream = mux.createStream('phishing')
|
||||
// connect "phishing" channel to warning system
|
||||
const phishingStream = extensionMux.createStream('phishing')
|
||||
phishingStream.once('data', redirectToPhishingWarning)
|
||||
|
||||
// ignore unused channels (handled by background, inpage)
|
||||
mux.ignoreStream('provider')
|
||||
mux.ignoreStream('publicConfig')
|
||||
// connect "publicApi" channel to submit page metadata
|
||||
const publicApiStream = extensionMux.createStream('publicApi')
|
||||
const background = await setupPublicApi(publicApiStream)
|
||||
|
||||
return { background }
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes listeners for requests to fully-enable the provider from the dapp context
|
||||
* and for full-provider approvals and rejections from the background script context. Dapps
|
||||
* should not post messages directly and should instead call provider.enable(), which
|
||||
* handles posting these messages internally.
|
||||
*/
|
||||
function listenForProviderRequest () {
|
||||
window.addEventListener('message', ({ source, data }) => {
|
||||
if (source !== window || !data || !data.type) { return }
|
||||
switch (data.type) {
|
||||
case 'ETHEREUM_ENABLE_PROVIDER':
|
||||
extension.runtime.sendMessage({
|
||||
action: 'init-provider-request',
|
||||
force: data.force,
|
||||
origin: source.location.hostname,
|
||||
siteImage: getSiteIcon(source),
|
||||
siteTitle: getSiteName(source),
|
||||
})
|
||||
break
|
||||
case 'ETHEREUM_IS_APPROVED':
|
||||
extension.runtime.sendMessage({
|
||||
action: 'init-is-approved',
|
||||
origin: source.location.hostname,
|
||||
})
|
||||
break
|
||||
case 'METAMASK_IS_UNLOCKED':
|
||||
extension.runtime.sendMessage({
|
||||
action: 'init-is-unlocked',
|
||||
})
|
||||
break
|
||||
function forwardTrafficBetweenMuxers (channelName, muxA, muxB) {
|
||||
const channelA = muxA.createStream(channelName)
|
||||
const channelB = muxB.createStream(channelName)
|
||||
pump(
|
||||
channelA,
|
||||
channelB,
|
||||
channelA,
|
||||
(err) => logStreamDisconnectWarning(`MetaMask muxed traffic for channel "${channelName}" failed.`, err)
|
||||
)
|
||||
}
|
||||
|
||||
async function setupPublicApi (outStream) {
|
||||
const api = {
|
||||
getSiteMetadata: (cb) => cb(null, getSiteMetadata()),
|
||||
}
|
||||
const dnode = Dnode(api)
|
||||
pump(
|
||||
outStream,
|
||||
dnode,
|
||||
outStream,
|
||||
(err) => {
|
||||
// report any error
|
||||
if (err) log.error(err)
|
||||
}
|
||||
})
|
||||
|
||||
extension.runtime.onMessage.addListener(({ action = '', isApproved, caching, isUnlocked, selectedAddress }) => {
|
||||
switch (action) {
|
||||
case 'approve-provider-request':
|
||||
isEnabled = true
|
||||
window.postMessage({ type: 'ethereumprovider', selectedAddress }, '*')
|
||||
break
|
||||
case 'approve-legacy-provider-request':
|
||||
isEnabled = true
|
||||
window.postMessage({ type: 'ethereumproviderlegacy', selectedAddress }, '*')
|
||||
break
|
||||
case 'reject-provider-request':
|
||||
window.postMessage({ type: 'ethereumprovider', error: 'User denied account authorization' }, '*')
|
||||
break
|
||||
case 'answer-is-approved':
|
||||
window.postMessage({ type: 'ethereumisapproved', isApproved, caching }, '*')
|
||||
break
|
||||
case 'answer-is-unlocked':
|
||||
window.postMessage({ type: 'metamaskisunlocked', isUnlocked }, '*')
|
||||
break
|
||||
case 'metamask-set-locked':
|
||||
isEnabled = false
|
||||
window.postMessage({ type: 'metamasksetlocked' }, '*')
|
||||
break
|
||||
case 'ethereum-ping-success':
|
||||
window.postMessage({ type: 'ethereumpingsuccess' }, '*')
|
||||
break
|
||||
case 'ethereum-ping-error':
|
||||
window.postMessage({ type: 'ethereumpingerror' }, '*')
|
||||
}
|
||||
})
|
||||
)
|
||||
const background = await new Promise(resolve => dnode.once('remote', resolve))
|
||||
return background
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if MetaMask is currently operating in "privacy mode", meaning
|
||||
* dapps must call ethereum.enable in order to access user accounts
|
||||
* Gets site metadata and returns it
|
||||
*
|
||||
*/
|
||||
function checkPrivacyMode () {
|
||||
extension.runtime.sendMessage({ action: 'init-privacy-request' })
|
||||
function getSiteMetadata () {
|
||||
// get metadata
|
||||
const metadata = {
|
||||
name: getSiteName(window),
|
||||
icon: getSiteIcon(window),
|
||||
}
|
||||
return metadata
|
||||
}
|
||||
|
||||
/**
|
||||
* Error handler for page to plugin stream disconnections
|
||||
* Error handler for page to extension stream disconnections
|
||||
*
|
||||
* @param {string} remoteLabel Remote stream name
|
||||
* @param {Error} err Stream connection error
|
||||
@ -301,6 +257,10 @@ function redirectToPhishingWarning () {
|
||||
})}`
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extracts a name for the site from the DOM
|
||||
*/
|
||||
function getSiteName (window) {
|
||||
const document = window.document
|
||||
const siteName = document.querySelector('head > meta[property="og:site_name"]')
|
||||
@ -316,6 +276,9 @@ function getSiteName (window) {
|
||||
return document.title
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts an icon for the site from the DOM
|
||||
*/
|
||||
function getSiteIcon (window) {
|
||||
const document = window.document
|
||||
|
||||
@ -333,3 +296,13 @@ function getSiteIcon (window) {
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise that resolves when the DOM is loaded (does not wait for images to load)
|
||||
*/
|
||||
async function domIsReady () {
|
||||
// already loaded
|
||||
if (['interactive', 'complete'].includes(document.readyState)) return
|
||||
// wait for load
|
||||
await new Promise(resolve => window.addEventListener('DOMContentLoaded', resolve, { once: true }))
|
||||
}
|
||||
|
@ -1,19 +0,0 @@
|
||||
const BlockTracker = require('eth-block-tracker')
|
||||
|
||||
/**
|
||||
* Creates a block tracker that sends platform events on success and failure
|
||||
*/
|
||||
module.exports = function createBlockTracker (args, platform) {
|
||||
const blockTracker = new BlockTracker(args)
|
||||
blockTracker.on('latest', () => {
|
||||
if (platform && platform.sendMessage) {
|
||||
platform.sendMessage({ action: 'ethereum-ping-success' })
|
||||
}
|
||||
})
|
||||
blockTracker.on('error', () => {
|
||||
if (platform && platform.sendMessage) {
|
||||
platform.sendMessage({ action: 'ethereum-ping-error' })
|
||||
}
|
||||
})
|
||||
return blockTracker
|
||||
}
|
@ -7,14 +7,14 @@ const createInflightMiddleware = require('eth-json-rpc-middleware/inflight-cache
|
||||
const createBlockTrackerInspectorMiddleware = require('eth-json-rpc-middleware/block-tracker-inspector')
|
||||
const providerFromMiddleware = require('eth-json-rpc-middleware/providerFromMiddleware')
|
||||
const createInfuraMiddleware = require('eth-json-rpc-infura')
|
||||
const createBlockTracker = require('./createBlockTracker')
|
||||
const BlockTracker = require('eth-block-tracker')
|
||||
|
||||
module.exports = createInfuraClient
|
||||
|
||||
function createInfuraClient ({ network, platform }) {
|
||||
function createInfuraClient ({ network }) {
|
||||
const infuraMiddleware = createInfuraMiddleware({ network, maxAttempts: 5, source: 'metamask' })
|
||||
const infuraProvider = providerFromMiddleware(infuraMiddleware)
|
||||
const blockTracker = createBlockTracker({ provider: infuraProvider }, platform)
|
||||
const blockTracker = new BlockTracker({ provider: infuraProvider })
|
||||
|
||||
const networkMiddleware = mergeMiddleware([
|
||||
createNetworkAndChainIdMiddleware({ network }),
|
||||
|
@ -5,14 +5,14 @@ const createBlockCacheMiddleware = require('eth-json-rpc-middleware/block-cache'
|
||||
const createInflightMiddleware = require('eth-json-rpc-middleware/inflight-cache')
|
||||
const createBlockTrackerInspectorMiddleware = require('eth-json-rpc-middleware/block-tracker-inspector')
|
||||
const providerFromMiddleware = require('eth-json-rpc-middleware/providerFromMiddleware')
|
||||
const createBlockTracker = require('./createBlockTracker')
|
||||
const BlockTracker = require('eth-block-tracker')
|
||||
|
||||
module.exports = createJsonRpcClient
|
||||
|
||||
function createJsonRpcClient ({ rpcUrl, platform }) {
|
||||
function createJsonRpcClient ({ rpcUrl }) {
|
||||
const fetchMiddleware = createFetchMiddleware({ rpcUrl })
|
||||
const blockProvider = providerFromMiddleware(fetchMiddleware)
|
||||
const blockTracker = createBlockTracker({ provider: blockProvider }, platform)
|
||||
const blockTracker = new BlockTracker({ provider: blockProvider })
|
||||
|
||||
const networkMiddleware = mergeMiddleware([
|
||||
createBlockRefRewriteMiddleware({ blockTracker }),
|
||||
|
@ -3,14 +3,14 @@ const createFetchMiddleware = require('eth-json-rpc-middleware/fetch')
|
||||
const createBlockRefRewriteMiddleware = require('eth-json-rpc-middleware/block-ref-rewrite')
|
||||
const createBlockTrackerInspectorMiddleware = require('eth-json-rpc-middleware/block-tracker-inspector')
|
||||
const providerFromMiddleware = require('eth-json-rpc-middleware/providerFromMiddleware')
|
||||
const createBlockTracker = require('./createBlockTracker')
|
||||
const BlockTracker = require('eth-block-tracker')
|
||||
|
||||
module.exports = createLocalhostClient
|
||||
|
||||
function createLocalhostClient ({ platform }) {
|
||||
function createLocalhostClient () {
|
||||
const fetchMiddleware = createFetchMiddleware({ rpcUrl: 'http://localhost:8545/' })
|
||||
const blockProvider = providerFromMiddleware(fetchMiddleware)
|
||||
const blockTracker = createBlockTracker({ provider: blockProvider, pollingInterval: 1000 }, platform)
|
||||
const blockTracker = new BlockTracker({ provider: blockProvider, pollingInterval: 1000 })
|
||||
|
||||
const networkMiddleware = mergeMiddleware([
|
||||
createBlockRefRewriteMiddleware({ blockTracker }),
|
||||
|
@ -46,9 +46,8 @@ const defaultNetworkConfig = {
|
||||
|
||||
module.exports = class NetworkController extends EventEmitter {
|
||||
|
||||
constructor (opts = {}, platform) {
|
||||
constructor (opts = {}) {
|
||||
super()
|
||||
this.platform = platform
|
||||
|
||||
// parse options
|
||||
const providerConfig = opts.provider || defaultProviderConfig
|
||||
@ -190,7 +189,7 @@ module.exports = class NetworkController extends EventEmitter {
|
||||
|
||||
_configureInfuraProvider ({ type }) {
|
||||
log.info('NetworkController - configureInfuraProvider', type)
|
||||
const networkClient = createInfuraClient({ network: type, platform: this.platform })
|
||||
const networkClient = createInfuraClient({ network: type })
|
||||
this._setNetworkClient(networkClient)
|
||||
// setup networkConfig
|
||||
var settings = {
|
||||
@ -201,13 +200,13 @@ module.exports = class NetworkController extends EventEmitter {
|
||||
|
||||
_configureLocalhostProvider () {
|
||||
log.info('NetworkController - configureLocalhostProvider')
|
||||
const networkClient = createLocalhostClient({ platform: this.platform })
|
||||
const networkClient = createLocalhostClient()
|
||||
this._setNetworkClient(networkClient)
|
||||
}
|
||||
|
||||
_configureStandardProvider ({ rpcUrl, chainId, ticker, nickname }) {
|
||||
log.info('NetworkController - configureStandardProvider', rpcUrl)
|
||||
const networkClient = createJsonRpcClient({ rpcUrl, platform: this.platform })
|
||||
const networkClient = createJsonRpcClient({ rpcUrl })
|
||||
// hack to add a 'rpc' network with chainId
|
||||
networks.networkList['rpc'] = {
|
||||
chainId: chainId,
|
||||
|
@ -1,9 +1,11 @@
|
||||
const ObservableStore = require('obs-store')
|
||||
const SafeEventEmitter = require('safe-event-emitter')
|
||||
const createAsyncMiddleware = require('json-rpc-engine/src/createAsyncMiddleware')
|
||||
|
||||
/**
|
||||
* A controller that services user-approved requests for a full Ethereum provider API
|
||||
*/
|
||||
class ProviderApprovalController {
|
||||
class ProviderApprovalController extends SafeEventEmitter {
|
||||
/**
|
||||
* Determines if caching is enabled
|
||||
*/
|
||||
@ -14,38 +16,43 @@ class ProviderApprovalController {
|
||||
*
|
||||
* @param {Object} [config] - Options to configure controller
|
||||
*/
|
||||
constructor ({ closePopup, keyringController, openPopup, platform, preferencesController, publicConfigStore } = {}) {
|
||||
constructor ({ closePopup, keyringController, openPopup, preferencesController } = {}) {
|
||||
super()
|
||||
this.approvedOrigins = {}
|
||||
this.closePopup = closePopup
|
||||
this.keyringController = keyringController
|
||||
this.openPopup = openPopup
|
||||
this.platform = platform
|
||||
this.preferencesController = preferencesController
|
||||
this.publicConfigStore = publicConfigStore
|
||||
this.store = new ObservableStore({
|
||||
providerRequests: [],
|
||||
})
|
||||
}
|
||||
|
||||
if (platform && platform.addMessageListener) {
|
||||
platform.addMessageListener(({ action = '', force, origin, siteTitle, siteImage }, { tab }) => {
|
||||
if (tab && tab.id) {
|
||||
switch (action) {
|
||||
case 'init-provider-request':
|
||||
this._handleProviderRequest(origin, siteTitle, siteImage, force, tab.id)
|
||||
break
|
||||
case 'init-is-approved':
|
||||
this._handleIsApproved(origin, tab.id)
|
||||
break
|
||||
case 'init-is-unlocked':
|
||||
this._handleIsUnlocked(tab.id)
|
||||
break
|
||||
case 'init-privacy-request':
|
||||
this._handlePrivacyRequest(tab.id)
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
/**
|
||||
* Called when a user approves access to a full Ethereum provider API
|
||||
*
|
||||
* @param {object} opts - opts for the middleware contains the origin for the middleware
|
||||
*/
|
||||
createMiddleware ({ origin, getSiteMetadata }) {
|
||||
return createAsyncMiddleware(async (req, res, next) => {
|
||||
// only handle requestAccounts
|
||||
if (req.method !== 'eth_requestAccounts') return next()
|
||||
// if already approved or privacy mode disabled, return early
|
||||
if (this.shouldExposeAccounts(origin)) {
|
||||
res.result = [this.preferencesController.getSelectedAddress()]
|
||||
return
|
||||
}
|
||||
// register the provider request
|
||||
const metadata = await getSiteMetadata(origin)
|
||||
this._handleProviderRequest(origin, metadata.name, metadata.icon, false, null)
|
||||
// wait for resolution of request
|
||||
const approved = await new Promise(resolve => this.once(`resolvedRequest:${origin}`, ({ approved }) => resolve(approved)))
|
||||
if (approved) {
|
||||
res.result = [this.preferencesController.getSelectedAddress()]
|
||||
} else {
|
||||
throw new Error('User denied account authorization')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -59,79 +66,37 @@ class ProviderApprovalController {
|
||||
this.store.updateState({ providerRequests: [{ origin, siteTitle, siteImage, tabID }] })
|
||||
const isUnlocked = this.keyringController.memStore.getState().isUnlocked
|
||||
if (!force && this.approvedOrigins[origin] && this.caching && isUnlocked) {
|
||||
this.approveProviderRequest(tabID)
|
||||
return
|
||||
}
|
||||
this.openPopup && this.openPopup()
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by a tab to determine if an origin has been approved in the past
|
||||
*
|
||||
* @param {string} origin - Origin of the window
|
||||
*/
|
||||
_handleIsApproved (origin, tabID) {
|
||||
this.platform && this.platform.sendMessage({
|
||||
action: 'answer-is-approved',
|
||||
isApproved: this.approvedOrigins[origin] && this.caching,
|
||||
caching: this.caching,
|
||||
}, { id: tabID })
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by a tab to determine if MetaMask is currently locked or unlocked
|
||||
*/
|
||||
_handleIsUnlocked (tabID) {
|
||||
const isUnlocked = this.keyringController.memStore.getState().isUnlocked
|
||||
this.platform && this.platform.sendMessage({ action: 'answer-is-unlocked', isUnlocked }, { id: tabID })
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to check privacy mode; if privacy mode is off, this will automatically enable the provider (legacy behavior)
|
||||
*/
|
||||
_handlePrivacyRequest (tabID) {
|
||||
const privacyMode = this.preferencesController.getFeatureFlags().privacyMode
|
||||
if (!privacyMode) {
|
||||
this.platform && this.platform.sendMessage({
|
||||
action: 'approve-legacy-provider-request',
|
||||
selectedAddress: this.publicConfigStore.getState().selectedAddress,
|
||||
}, { id: tabID })
|
||||
this.publicConfigStore.emit('update', this.publicConfigStore.getState())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a user approves access to a full Ethereum provider API
|
||||
*
|
||||
* @param {string} tabID - ID of the target window that approved provider access
|
||||
* @param {string} origin - origin of the domain that had provider access approved
|
||||
*/
|
||||
approveProviderRequest (tabID) {
|
||||
approveProviderRequestByOrigin (origin) {
|
||||
this.closePopup && this.closePopup()
|
||||
const requests = this.store.getState().providerRequests
|
||||
const origin = requests.find(request => request.tabID === tabID).origin
|
||||
this.platform && this.platform.sendMessage({
|
||||
action: 'approve-provider-request',
|
||||
selectedAddress: this.publicConfigStore.getState().selectedAddress,
|
||||
}, { id: tabID })
|
||||
this.publicConfigStore.emit('update', this.publicConfigStore.getState())
|
||||
const providerRequests = requests.filter(request => request.tabID !== tabID)
|
||||
const providerRequests = requests.filter(request => request.origin !== origin)
|
||||
this.store.updateState({ providerRequests })
|
||||
this.approvedOrigins[origin] = true
|
||||
this.emit(`resolvedRequest:${origin}`, { approved: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a tab rejects access to a full Ethereum provider API
|
||||
*
|
||||
* @param {string} tabID - ID of the target window that rejected provider access
|
||||
* @param {string} origin - origin of the domain that had provider access approved
|
||||
*/
|
||||
rejectProviderRequest (tabID) {
|
||||
rejectProviderRequestByOrigin (origin) {
|
||||
this.closePopup && this.closePopup()
|
||||
const requests = this.store.getState().providerRequests
|
||||
const origin = requests.find(request => request.tabID === tabID).origin
|
||||
this.platform && this.platform.sendMessage({ action: 'reject-provider-request' }, { id: tabID })
|
||||
const providerRequests = requests.filter(request => request.tabID !== tabID)
|
||||
const providerRequests = requests.filter(request => request.origin !== origin)
|
||||
this.store.updateState({ providerRequests })
|
||||
delete this.approvedOrigins[origin]
|
||||
this.emit(`resolvedRequest:${origin}`, { approved: false })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -149,16 +114,10 @@ class ProviderApprovalController {
|
||||
*/
|
||||
shouldExposeAccounts (origin) {
|
||||
const privacyMode = this.preferencesController.getFeatureFlags().privacyMode
|
||||
return !privacyMode || this.approvedOrigins[origin]
|
||||
const result = !privacyMode || Boolean(this.approvedOrigins[origin])
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells all tabs that MetaMask is now locked. This is primarily used to set
|
||||
* internal flags in the contentscript and inpage script.
|
||||
*/
|
||||
setLocked () {
|
||||
this.platform.sendMessage({ action: 'metamask-set-locked' })
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ProviderApprovalController
|
||||
|
@ -4,18 +4,10 @@ class StandardProvider {
|
||||
|
||||
constructor (provider) {
|
||||
this._provider = provider
|
||||
this._onMessage('ethereumpingerror', this._onClose.bind(this))
|
||||
this._onMessage('ethereumpingsuccess', this._onConnect.bind(this))
|
||||
window.addEventListener('load', () => {
|
||||
this._subscribe()
|
||||
this._ping()
|
||||
})
|
||||
}
|
||||
|
||||
_onMessage (type, handler) {
|
||||
window.addEventListener('message', function ({ data }) {
|
||||
if (!data || data.type !== type) return
|
||||
handler.apply(this, arguments)
|
||||
this._subscribe()
|
||||
// indicate that we've connected, mostly just for standard compliance
|
||||
setTimeout(() => {
|
||||
this._onConnect()
|
||||
})
|
||||
}
|
||||
|
||||
@ -34,15 +26,6 @@ class StandardProvider {
|
||||
this._isConnected = true
|
||||
}
|
||||
|
||||
async _ping () {
|
||||
try {
|
||||
await this.send('net_version')
|
||||
window.postMessage({ type: 'ethereumpingsuccess' }, '*')
|
||||
} catch (error) {
|
||||
window.postMessage({ type: 'ethereumpingerror' }, '*')
|
||||
}
|
||||
}
|
||||
|
||||
_subscribe () {
|
||||
this._provider.on('data', (error, { method, params }) => {
|
||||
if (!error && method === 'eth_subscription') {
|
||||
@ -59,11 +42,9 @@ class StandardProvider {
|
||||
* @returns {Promise<*>} Promise resolving to the result if successful
|
||||
*/
|
||||
send (method, params = []) {
|
||||
if (method === 'eth_requestAccounts') return this._provider.enable()
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
this._provider.sendAsync({ method, params, beta: true }, (error, response) => {
|
||||
this._provider.sendAsync({ id: 1, jsonrpc: '2.0', method, params }, (error, response) => {
|
||||
error = error || response.error
|
||||
error ? reject(error) : resolve(response)
|
||||
})
|
||||
|
@ -7,32 +7,12 @@ const setupDappAutoReload = require('./lib/auto-reload.js')
|
||||
const MetamaskInpageProvider = require('metamask-inpage-provider')
|
||||
const createStandardProvider = require('./createStandardProvider').default
|
||||
|
||||
let isEnabled = false
|
||||
let warned = false
|
||||
let providerHandle
|
||||
let isApprovedHandle
|
||||
let isUnlockedHandle
|
||||
|
||||
restoreContextAfterImports()
|
||||
|
||||
log.setDefaultLevel(process.env.METAMASK_DEBUG ? 'debug' : 'warn')
|
||||
|
||||
/**
|
||||
* Adds a postMessage listener for a specific message type
|
||||
*
|
||||
* @param {string} messageType - postMessage type to listen for
|
||||
* @param {Function} handler - event handler
|
||||
* @param {boolean} remove - removes this handler after being triggered
|
||||
*/
|
||||
function onMessage (messageType, callback, remove) {
|
||||
const handler = function ({ data }) {
|
||||
if (!data || data.type !== messageType) { return }
|
||||
remove && window.removeEventListener('message', handler)
|
||||
callback.apply(window, arguments)
|
||||
}
|
||||
window.addEventListener('message', handler)
|
||||
}
|
||||
|
||||
//
|
||||
// setup plugin communication
|
||||
//
|
||||
@ -49,45 +29,16 @@ const inpageProvider = new MetamaskInpageProvider(metamaskStream)
|
||||
// set a high max listener count to avoid unnecesary warnings
|
||||
inpageProvider.setMaxListeners(100)
|
||||
|
||||
// set up a listener for when MetaMask is locked
|
||||
onMessage('metamasksetlocked', () => { isEnabled = false })
|
||||
|
||||
// set up a listener for privacy mode responses
|
||||
onMessage('ethereumproviderlegacy', ({ data: { selectedAddress } }) => {
|
||||
isEnabled = true
|
||||
setTimeout(() => {
|
||||
inpageProvider.publicConfigStore.updateState({ selectedAddress })
|
||||
}, 0)
|
||||
}, true)
|
||||
|
||||
// augment the provider with its enable method
|
||||
inpageProvider.enable = function ({ force } = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
providerHandle = ({ data: { error, selectedAddress } }) => {
|
||||
if (typeof error !== 'undefined') {
|
||||
reject({
|
||||
message: error,
|
||||
code: 4001,
|
||||
})
|
||||
inpageProvider.sendAsync({ method: 'eth_requestAccounts', params: [force] }, (error, response) => {
|
||||
if (error) {
|
||||
reject(error)
|
||||
} else {
|
||||
window.removeEventListener('message', providerHandle)
|
||||
setTimeout(() => {
|
||||
inpageProvider.publicConfigStore.updateState({ selectedAddress })
|
||||
}, 0)
|
||||
|
||||
// wait for the background to update with an account
|
||||
inpageProvider.sendAsync({ method: 'eth_accounts', params: [] }, (error, response) => {
|
||||
if (error) {
|
||||
reject(error)
|
||||
} else {
|
||||
isEnabled = true
|
||||
resolve(response.result)
|
||||
}
|
||||
})
|
||||
resolve(response.result)
|
||||
}
|
||||
}
|
||||
onMessage('ethereumprovider', providerHandle, true)
|
||||
window.postMessage({ type: 'ETHEREUM_ENABLE_PROVIDER', force }, '*')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@ -98,31 +49,23 @@ inpageProvider.autoRefreshOnNetworkChange = true
|
||||
// add metamask-specific convenience methods
|
||||
inpageProvider._metamask = new Proxy({
|
||||
/**
|
||||
* Determines if this domain is currently enabled
|
||||
* Synchronously determines if this domain is currently enabled, with a potential false negative if called to soon
|
||||
*
|
||||
* @returns {boolean} - true if this domain is currently enabled
|
||||
* @returns {boolean} - returns true if this domain is currently enabled
|
||||
*/
|
||||
isEnabled: function () {
|
||||
return isEnabled
|
||||
const { isEnabled } = inpageProvider.publicConfigStore.getState()
|
||||
return Boolean(isEnabled)
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines if this domain has been previously approved
|
||||
* Asynchronously determines if this domain is currently enabled
|
||||
*
|
||||
* @returns {Promise<boolean>} - Promise resolving to true if this domain has been previously approved
|
||||
* @returns {Promise<boolean>} - Promise resolving to true if this domain is currently enabled
|
||||
*/
|
||||
isApproved: function () {
|
||||
return new Promise((resolve) => {
|
||||
isApprovedHandle = ({ data: { caching, isApproved } }) => {
|
||||
if (caching) {
|
||||
resolve(!!isApproved)
|
||||
} else {
|
||||
resolve(false)
|
||||
}
|
||||
}
|
||||
onMessage('ethereumisapproved', isApprovedHandle, true)
|
||||
window.postMessage({ type: 'ETHEREUM_IS_APPROVED' }, '*')
|
||||
})
|
||||
isApproved: async function () {
|
||||
const { isEnabled } = await getPublicConfigWhenReady()
|
||||
return Boolean(isEnabled)
|
||||
},
|
||||
|
||||
/**
|
||||
@ -130,14 +73,9 @@ inpageProvider._metamask = new Proxy({
|
||||
*
|
||||
* @returns {Promise<boolean>} - Promise resolving to true if MetaMask is currently unlocked
|
||||
*/
|
||||
isUnlocked: function () {
|
||||
return new Promise((resolve) => {
|
||||
isUnlockedHandle = ({ data: { isUnlocked } }) => {
|
||||
resolve(!!isUnlocked)
|
||||
}
|
||||
onMessage('metamaskisunlocked', isUnlockedHandle, true)
|
||||
window.postMessage({ type: 'METAMASK_IS_UNLOCKED' }, '*')
|
||||
})
|
||||
isUnlocked: async function () {
|
||||
const { isUnlocked } = await getPublicConfigWhenReady()
|
||||
return Boolean(isUnlocked)
|
||||
},
|
||||
}, {
|
||||
get: function (obj, prop) {
|
||||
@ -149,6 +87,19 @@ inpageProvider._metamask = new Proxy({
|
||||
},
|
||||
})
|
||||
|
||||
// publicConfig isn't populated until we get a message from background.
|
||||
// Using this getter will ensure the state is available
|
||||
async function getPublicConfigWhenReady () {
|
||||
const store = inpageProvider.publicConfigStore
|
||||
let state = store.getState()
|
||||
// if state is missing, wait for first update
|
||||
if (!state.networkVersion) {
|
||||
state = await new Promise(resolve => store.once('update', resolve))
|
||||
console.log('new state', state)
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
// Work around for web3@1.0 deleting the bound `sendAsync` but not the unbound
|
||||
// `sendAsync` method on the prototype, causing `this` reference issues with drizzle
|
||||
const proxiedInpageProvider = new Proxy(inpageProvider, {
|
||||
@ -159,19 +110,6 @@ const proxiedInpageProvider = new Proxy(inpageProvider, {
|
||||
|
||||
window.ethereum = createStandardProvider(proxiedInpageProvider)
|
||||
|
||||
// detect eth_requestAccounts and pipe to enable for now
|
||||
function detectAccountRequest (method) {
|
||||
const originalMethod = inpageProvider[method]
|
||||
inpageProvider[method] = function ({ method }) {
|
||||
if (method === 'eth_requestAccounts') {
|
||||
return window.ethereum.enable()
|
||||
}
|
||||
return originalMethod.apply(this, arguments)
|
||||
}
|
||||
}
|
||||
detectAccountRequest('send')
|
||||
detectAccountRequest('sendAsync')
|
||||
|
||||
//
|
||||
// setup web3
|
||||
//
|
||||
|
16
app/scripts/lib/createDnodeRemoteGetter.js
Normal file
16
app/scripts/lib/createDnodeRemoteGetter.js
Normal file
@ -0,0 +1,16 @@
|
||||
module.exports = createDnodeRemoteGetter
|
||||
|
||||
function createDnodeRemoteGetter (dnode) {
|
||||
let remote
|
||||
|
||||
dnode.once('remote', (_remote) => {
|
||||
remote = _remote
|
||||
})
|
||||
|
||||
async function getRemote () {
|
||||
if (remote) return remote
|
||||
return await new Promise(resolve => dnode.once('remote', resolve))
|
||||
}
|
||||
|
||||
return getRemote
|
||||
}
|
@ -7,8 +7,10 @@
|
||||
const EventEmitter = require('events')
|
||||
const pump = require('pump')
|
||||
const Dnode = require('dnode')
|
||||
const pify = require('pify')
|
||||
const ObservableStore = require('obs-store')
|
||||
const ComposableObservableStore = require('./lib/ComposableObservableStore')
|
||||
const createDnodeRemoteGetter = require('./lib/createDnodeRemoteGetter')
|
||||
const asStream = require('obs-store/lib/asStream')
|
||||
const AccountTracker = require('./lib/account-tracker')
|
||||
const RpcEngine = require('json-rpc-engine')
|
||||
@ -87,7 +89,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
this.createVaultMutex = new Mutex()
|
||||
|
||||
// network store
|
||||
this.networkController = new NetworkController(initState.NetworkController, this.platform)
|
||||
this.networkController = new NetworkController(initState.NetworkController)
|
||||
|
||||
// preferences controller
|
||||
this.preferencesController = new PreferencesController({
|
||||
@ -235,15 +237,17 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
this.messageManager = new MessageManager()
|
||||
this.personalMessageManager = new PersonalMessageManager()
|
||||
this.typedMessageManager = new TypedMessageManager({ networkController: this.networkController })
|
||||
this.publicConfigStore = this.initPublicConfigStore()
|
||||
|
||||
// ensure isClientOpenAndUnlocked is updated when memState updates
|
||||
this.on('update', (memState) => {
|
||||
this.isClientOpenAndUnlocked = memState.isUnlocked && this._isClientOpen
|
||||
})
|
||||
|
||||
this.providerApprovalController = new ProviderApprovalController({
|
||||
closePopup: opts.closePopup,
|
||||
keyringController: this.keyringController,
|
||||
openPopup: opts.openPopup,
|
||||
platform: opts.platform,
|
||||
preferencesController: this.preferencesController,
|
||||
publicConfigStore: this.publicConfigStore,
|
||||
})
|
||||
|
||||
this.store.updateStructure({
|
||||
@ -322,22 +326,32 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
* Constructor helper: initialize a public config store.
|
||||
* This store is used to make some config info available to Dapps synchronously.
|
||||
*/
|
||||
initPublicConfigStore () {
|
||||
// get init state
|
||||
createPublicConfigStore ({ checkIsEnabled }) {
|
||||
// subset of state for metamask inpage provider
|
||||
const publicConfigStore = new ObservableStore()
|
||||
|
||||
// memStore -> transform -> publicConfigStore
|
||||
this.on('update', (memState) => {
|
||||
this.isClientOpenAndUnlocked = memState.isUnlocked && this._isClientOpen
|
||||
// setup memStore subscription hooks
|
||||
this.on('update', updatePublicConfigStore)
|
||||
updatePublicConfigStore(this.getState())
|
||||
|
||||
publicConfigStore.destroy = () => {
|
||||
this.removeEventListener('update', updatePublicConfigStore)
|
||||
}
|
||||
|
||||
function updatePublicConfigStore (memState) {
|
||||
const publicState = selectPublicState(memState)
|
||||
publicConfigStore.putState(publicState)
|
||||
})
|
||||
}
|
||||
|
||||
function selectPublicState (memState) {
|
||||
function selectPublicState ({ isUnlocked, selectedAddress, network, completedOnboarding }) {
|
||||
const isEnabled = checkIsEnabled()
|
||||
const isReady = isUnlocked && isEnabled
|
||||
const result = {
|
||||
selectedAddress: memState.isUnlocked ? memState.selectedAddress : undefined,
|
||||
networkVersion: memState.network,
|
||||
onboardingcomplete: memState.completedOnboarding,
|
||||
isUnlocked,
|
||||
isEnabled,
|
||||
selectedAddress: isReady ? selectedAddress : undefined,
|
||||
networkVersion: network,
|
||||
onboardingcomplete: completedOnboarding,
|
||||
}
|
||||
return result
|
||||
}
|
||||
@ -477,9 +491,10 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
signTypedMessage: nodeify(this.signTypedMessage, this),
|
||||
cancelTypedMessage: this.cancelTypedMessage.bind(this),
|
||||
|
||||
approveProviderRequest: providerApprovalController.approveProviderRequest.bind(providerApprovalController),
|
||||
// provider approval
|
||||
approveProviderRequestByOrigin: providerApprovalController.approveProviderRequestByOrigin.bind(providerApprovalController),
|
||||
rejectProviderRequestByOrigin: providerApprovalController.rejectProviderRequestByOrigin.bind(providerApprovalController),
|
||||
clearApprovedOrigins: providerApprovalController.clearApprovedOrigins.bind(providerApprovalController),
|
||||
rejectProviderRequest: providerApprovalController.rejectProviderRequest.bind(providerApprovalController),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1296,8 +1311,9 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
// setup multiplexing
|
||||
const mux = setupMultiplex(connectionStream)
|
||||
// connect features
|
||||
this.setupProviderConnection(mux.createStream('provider'), originDomain)
|
||||
this.setupPublicConfig(mux.createStream('publicConfig'))
|
||||
const publicApi = this.setupPublicApi(mux.createStream('publicApi'), originDomain)
|
||||
this.setupProviderConnection(mux.createStream('provider'), originDomain, publicApi)
|
||||
this.setupPublicConfig(mux.createStream('publicConfig'), originDomain)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1370,7 +1386,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
* @param {*} outStream - The stream to provide over.
|
||||
* @param {string} origin - The URI of the requesting resource.
|
||||
*/
|
||||
setupProviderConnection (outStream, origin) {
|
||||
setupProviderConnection (outStream, origin, publicApi) {
|
||||
// setup json rpc engine stack
|
||||
const engine = new RpcEngine()
|
||||
const provider = this.provider
|
||||
@ -1390,6 +1406,11 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
engine.push(subscriptionManager.middleware)
|
||||
// watch asset
|
||||
engine.push(this.preferencesController.requestWatchAsset.bind(this.preferencesController))
|
||||
// requestAccounts
|
||||
engine.push(this.providerApprovalController.createMiddleware({
|
||||
origin,
|
||||
getSiteMetadata: publicApi && publicApi.getSiteMetadata,
|
||||
}))
|
||||
// forward to metamask primary provider
|
||||
engine.push(providerAsMiddleware(provider))
|
||||
|
||||
@ -1418,18 +1439,56 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
*
|
||||
* @param {*} outStream - The stream to provide public config over.
|
||||
*/
|
||||
setupPublicConfig (outStream) {
|
||||
const configStream = asStream(this.publicConfigStore)
|
||||
setupPublicConfig (outStream, originDomain) {
|
||||
const configStore = this.createPublicConfigStore({
|
||||
// check the providerApprovalController's approvedOrigins
|
||||
checkIsEnabled: () => this.providerApprovalController.shouldExposeAccounts(originDomain),
|
||||
})
|
||||
const configStream = asStream(configStore)
|
||||
|
||||
pump(
|
||||
configStream,
|
||||
outStream,
|
||||
(err) => {
|
||||
configStore.destroy()
|
||||
configStream.destroy()
|
||||
if (err) log.error(err)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A method for providing our public api over a stream.
|
||||
* This includes a method for setting site metadata like title and image
|
||||
*
|
||||
* @param {*} outStream - The stream to provide the api over.
|
||||
*/
|
||||
setupPublicApi (outStream, originDomain) {
|
||||
const dnode = Dnode()
|
||||
// connect dnode api to remote connection
|
||||
pump(
|
||||
outStream,
|
||||
dnode,
|
||||
outStream,
|
||||
(err) => {
|
||||
// report any error
|
||||
if (err) log.error(err)
|
||||
}
|
||||
)
|
||||
|
||||
const getRemote = createDnodeRemoteGetter(dnode)
|
||||
|
||||
const publicApi = {
|
||||
// wrap with an await remote
|
||||
getSiteMetadata: async () => {
|
||||
const remote = await getRemote()
|
||||
return await pify(remote.getSiteMetadata)()
|
||||
},
|
||||
}
|
||||
|
||||
return publicApi
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a KeyringController update
|
||||
* @param {object} state the KC state
|
||||
@ -1734,7 +1793,6 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
* Locks MetaMask
|
||||
*/
|
||||
setLocked () {
|
||||
this.providerApprovalController.setLocked()
|
||||
return this.keyringController.setLocked()
|
||||
}
|
||||
}
|
||||
|
@ -60,20 +60,6 @@ class ExtensionPlatform {
|
||||
}
|
||||
}
|
||||
|
||||
addMessageListener (cb) {
|
||||
extension.runtime.onMessage.addListener(cb)
|
||||
}
|
||||
|
||||
sendMessage (message, query = {}) {
|
||||
const id = query.id
|
||||
delete query.id
|
||||
extension.tabs.query({ ...query }, tabs => {
|
||||
tabs.forEach(tab => {
|
||||
extension.tabs.sendMessage(id || tab.id, message)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
_showConfirmedTransaction (txMeta) {
|
||||
|
||||
this._subscribeToNotificationClicked()
|
||||
|
@ -136,7 +136,6 @@
|
||||
"obs-store": "^3.0.2",
|
||||
"percentile": "^1.2.0",
|
||||
"pify": "^3.0.0",
|
||||
"ping-pong-stream": "^1.0.0",
|
||||
"pojo-migrator": "^2.1.0",
|
||||
"polyfill-crypto.getrandomvalues": "^1.0.0",
|
||||
"post-message-stream": "^3.0.0",
|
||||
|
@ -5,12 +5,11 @@ import { PageContainerFooter } from '../../ui/page-container'
|
||||
|
||||
export default class ProviderPageContainer extends PureComponent {
|
||||
static propTypes = {
|
||||
approveProviderRequest: PropTypes.func.isRequired,
|
||||
approveProviderRequestByOrigin: PropTypes.func.isRequired,
|
||||
rejectProviderRequestByOrigin: PropTypes.func.isRequired,
|
||||
origin: PropTypes.string.isRequired,
|
||||
rejectProviderRequest: PropTypes.func.isRequired,
|
||||
siteImage: PropTypes.string,
|
||||
siteTitle: PropTypes.string.isRequired,
|
||||
tabID: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
@ -29,7 +28,7 @@ export default class ProviderPageContainer extends PureComponent {
|
||||
}
|
||||
|
||||
onCancel = () => {
|
||||
const { tabID, rejectProviderRequest } = this.props
|
||||
const { origin, rejectProviderRequestByOrigin } = this.props
|
||||
this.context.metricsEvent({
|
||||
eventOpts: {
|
||||
category: 'Auth',
|
||||
@ -37,11 +36,11 @@ export default class ProviderPageContainer extends PureComponent {
|
||||
name: 'Canceled',
|
||||
},
|
||||
})
|
||||
rejectProviderRequest(tabID)
|
||||
rejectProviderRequestByOrigin(origin)
|
||||
}
|
||||
|
||||
onSubmit = () => {
|
||||
const { approveProviderRequest, tabID } = this.props
|
||||
const { approveProviderRequestByOrigin, origin } = this.props
|
||||
this.context.metricsEvent({
|
||||
eventOpts: {
|
||||
category: 'Auth',
|
||||
@ -49,7 +48,7 @@ export default class ProviderPageContainer extends PureComponent {
|
||||
name: 'Confirmed',
|
||||
},
|
||||
})
|
||||
approveProviderRequest(tabID)
|
||||
approveProviderRequestByOrigin(origin)
|
||||
}
|
||||
|
||||
render () {
|
||||
|
@ -4,9 +4,9 @@ import ProviderPageContainer from '../../components/app/provider-page-container'
|
||||
|
||||
export default class ProviderApproval extends Component {
|
||||
static propTypes = {
|
||||
approveProviderRequest: PropTypes.func.isRequired,
|
||||
approveProviderRequestByOrigin: PropTypes.func.isRequired,
|
||||
rejectProviderRequestByOrigin: PropTypes.func.isRequired,
|
||||
providerRequest: PropTypes.object.isRequired,
|
||||
rejectProviderRequest: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
@ -14,13 +14,13 @@ export default class ProviderApproval extends Component {
|
||||
};
|
||||
|
||||
render () {
|
||||
const { approveProviderRequest, providerRequest, rejectProviderRequest } = this.props
|
||||
const { approveProviderRequestByOrigin, providerRequest, rejectProviderRequestByOrigin } = this.props
|
||||
return (
|
||||
<ProviderPageContainer
|
||||
approveProviderRequest={approveProviderRequest}
|
||||
approveProviderRequestByOrigin={approveProviderRequestByOrigin}
|
||||
rejectProviderRequestByOrigin={rejectProviderRequestByOrigin}
|
||||
origin={providerRequest.origin}
|
||||
tabID={providerRequest.tabID}
|
||||
rejectProviderRequest={rejectProviderRequest}
|
||||
siteImage={providerRequest.siteImage}
|
||||
siteTitle={providerRequest.siteTitle}
|
||||
/>
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { connect } from 'react-redux'
|
||||
import ProviderApproval from './provider-approval.component'
|
||||
import { approveProviderRequest, rejectProviderRequest } from '../../store/actions'
|
||||
import { approveProviderRequestByOrigin, rejectProviderRequestByOrigin } from '../../store/actions'
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return {
|
||||
approveProviderRequest: tabID => dispatch(approveProviderRequest(tabID)),
|
||||
rejectProviderRequest: tabID => dispatch(rejectProviderRequest(tabID)),
|
||||
approveProviderRequestByOrigin: origin => dispatch(approveProviderRequestByOrigin(origin)),
|
||||
rejectProviderRequestByOrigin: origin => dispatch(rejectProviderRequestByOrigin(origin)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -343,8 +343,8 @@ var actions = {
|
||||
createCancelTransaction,
|
||||
createSpeedUpTransaction,
|
||||
|
||||
approveProviderRequest,
|
||||
rejectProviderRequest,
|
||||
approveProviderRequestByOrigin,
|
||||
rejectProviderRequestByOrigin,
|
||||
clearApprovedOrigins,
|
||||
|
||||
setFirstTimeFlowType,
|
||||
@ -2680,15 +2680,15 @@ function setPendingTokens (pendingTokens) {
|
||||
}
|
||||
}
|
||||
|
||||
function approveProviderRequest (tabID) {
|
||||
function approveProviderRequestByOrigin (origin) {
|
||||
return (dispatch) => {
|
||||
background.approveProviderRequest(tabID)
|
||||
background.approveProviderRequestByOrigin(origin)
|
||||
}
|
||||
}
|
||||
|
||||
function rejectProviderRequest (tabID) {
|
||||
function rejectProviderRequestByOrigin (origin) {
|
||||
return (dispatch) => {
|
||||
background.rejectProviderRequest(tabID)
|
||||
background.rejectProviderRequestByOrigin(origin)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user