-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Compat w/ Brave to support multi web3 providers #7728
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We try to avoid adding complexity to the metamask-controller, which is ideally not platform-aware. I recommend adding the platform-dependent logic either in background.js
or as a function passed in the platform
parameter for the MM-controller.
app/scripts/metamask-controller.js
Outdated
@@ -74,6 +76,21 @@ module.exports = class MetamaskController extends EventEmitter { | |||
*/ | |||
constructor (opts) { | |||
super() | |||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We currently try to keep platform-specific API usage outside of the metamask-controller, so that it can be more platform-portable.
The way we currently do this is using the platform
object passed into the MetaMask controller's constructor options.
In this case, I think an easier (and more flexible) approach would be putting this logic in background.js
, which already has a runtime.onConnectExternal.addListener
callback, which I think is a perfect place to add this conditional platform-distinguishing logic:
https://github.com/MetaMask/metamask-extension/blob/develop/app/scripts/background.js#L320
f72db47
to
6a12d7e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have some reservations about the injectScript
call being moved into a message handler. This would make it asynchronous, I believe? Not only is the script injection a performance-critical moment (as a delay here means delaying initialization), it also means that we're no longer guaranteeing the globals injected by MetaMask are present before a dapp tries to access them. e.g. it may be possible, if the message response is sufficiently slow, for the dapp to run prior to the script being injected. This could break many dapps.
Could you move this braveWallet
API into the contentscript instead? If we could check extension.braveWallet
synchronously directly in the contentscript, it'd eliminate both of the above concerns.
This implements the spec changes needed for better compatibility with Brave: brave/brave-browser#7503 It makes it so both extensions can co-exist.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, this is fantastic! This addresses my earlier concerns.
@@ -1,3 +1,5 @@ | |||
/*global chrome*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was going to suggest that you use extensionizer
instead, but that wouldn't work - that doesn't expose the braveWallet
API.
Maybe we should update extensionizer
to add that API? 🤔 In the meantime, this seems OK
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If braveWallet
is a similar API to the chrome
/extension
APIs that are common in browsers, then it would be a good fit. By the name, it sounds more like a provider..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
braveWallet
is an additional API. It would need to be added to the list of APIs in extensionizer
. At the moment, that package only exports the APIs in that list.
It's not a cross-browser API of course, so I wasn't sure whether it was a good fit for that package.
start() | ||
// If this is Brave, it requires coordination to know if we should be the | ||
// web3 provider. | ||
if (chrome.braveWallet && chrome.braveWallet.getWeb3Provider) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (chrome.braveWallet && chrome.braveWallet.getWeb3Provider) { | |
if (chrome && chrome.braveWallet && chrome.braveWallet.getWeb3Provider) { |
Correct me if I'm wrong, but I suspect that the chrome
global wouldn't exist on Firefox? I think we need to check for that here too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, since chrome
doesn't exist in firefox, this first clause would throw an undefined error. Either needs to be typeof chrome !== 'undefined'
or you could import extensionizer and check extension.braveWallet
to avoid this problem.
@Gudahtt @danfinlay I updated it so it's all done from the content-script now but it's still async. I'm looking for a way to do it sync still. I'll update again once I figure that out. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like it would crash Firefox in its current form.
start() | ||
// If this is Brave, it requires coordination to know if we should be the | ||
// web3 provider. | ||
if (chrome.braveWallet && chrome.braveWallet.getWeb3Provider) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, since chrome
doesn't exist in firefox, this first clause would throw an undefined error. Either needs to be typeof chrome !== 'undefined'
or you could import extensionizer and check extension.braveWallet
to avoid this problem.
@@ -1,3 +1,5 @@ | |||
/*global chrome*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If braveWallet
is a similar API to the chrome
/extension
APIs that are common in browsers, then it would be a good fit. By the name, it sounds more like a provider..
if (chrome.braveWallet && chrome.braveWallet.getWeb3Provider) { | ||
chrome.braveWallet.getWeb3Provider((provider) => { | ||
if (provider === extension.runtime.id) { | ||
injectScript(inpageBundle) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @Gudahtt that injecting async is asking for trouble, so I really recommend addressing that for Brave's sake. Since this doesn't introduce any async operation to our WebExtension-based injection, I'm okay with this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep I'll get it working sync.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll add the typeof check on chrome too.
I think I have a way to do this entirely without any changes to the extension itself. It would disable the particular content script if the extension is not the web3 provider. What do you think of that approach? Currently on MetaMask and the Brave fork of MetaMask would be supported so there'd be overlap functionality for things like phishing detection. A second content script could be addd for outside of web3 functionality later if we wanted which wouldn't be disabled when the installed extension is not the web3 provider. |
@bbondy That sounds great! That would be a lot simpler. The So would it be possible to leave that API accessible from the background? I realize I was the one to asked it to be moved to the contentscript, sorry about that 😅. |
@Gudahtt yep that's a great idea so you can still inform the user based on the same condition I did in this PR. |
Confirming the new approach with no extension changes will work. It'll dynamically disable the content script. We might want to do work in the future to break out functionality into 2 different content scripts depending on if it is web3 related or not, but I think that's low priority. |
FYI that this landed on Brave Nightly and will be on Dev channel within a few days. Within 6 weeks it'll on Release channel assuming we don't uplift it. |
This implements the spec changes needed for better compatibility with Brave:
brave/brave-browser#7503
It makes it so both extensions can co-exist.
This might not be the cleanest way to accomplish it. Happy to receive review comments and I'll improve it. Thanks!