在自己开发的插件中调用MetaMask插件来实现以太坊账户登录和交互
背景
最近想制作一个浏览器新标签页导航器(类似于infinitytab)的chrome插件,其中,希望在插件中调用MetaMask来作为账户登录。
在开发插件时,遇到了一个致命的问题!我们都知道,如果浏览器安装了MetaMask插件, 那么会默认在浏览器window环境上注入window.ethereum
,如下图所示
这个是MetaMask提供的默认Provider,可以访问以太坊账户的信息。
但是,在新标签页,MetaMask是不注入这个变量的,也就是在chrome-extension://
这个地址下面,是没有注入Provider的。
这就导致了当我们自己开发了一个新标签工具,想要直接调用MetaMask的账户登录时,是没什么反应的。
怎么解决?
window.ethereum
如何注入浏览器的?
首先想到的解决方法,试看一下MetaMask是怎么注入到浏览器中这个window.ethereum
变量的。因此,我找到了 MetaMask浏览器插件的源码:https://github.com/MetaMask/metamask-extension
metamask-extension
在这个插件中,我先按照window.ethereum
搜索了一下,没有任何的发现:
于是,开始寻找是否给页面注入了js脚本,最终在app/scripts/contentscript.js
文件中看到了injectScript
函数:
从这个文件我们看到使用到了inpage.js
文件,于是在app/scripts/inpage.js
文件,看到了initializeProvider
函数,猜测大概率跟这个函数有关。而这个函数是从@metamask/providers/dist/initializeInpageProvider
中导入的,于是,我找到了@metamask/providers
包的源码。
https://github.com/MetaMask/providers
在这个包中,找到了initializeInpageProvider
的源码,其中用到了setGlobalProvider
函数
在这个文件的下方我们找到了这个setGlobalProvider
函数,
终于,原来是在这里,MetaMask将provider实例注入到了 window.ethereum
中。
如何在自己插件中使用
但是,我们怎么在自己的插件中获得provider的实例并且注入window.ethereum
呢?毕竟我们又无法更改MetaMask的源码。最终,还是从MetaMask官方插件的Github源码 https://github.com/MetaMask/metamask-extension的issues中,找到了这条回答:https://github.com/MetaMask/metamask-extension/issues/12444#issuecomment-1029338757:
意思是说他们有个extension-provider的库也许可以解决这个问题。
那么,就继续解决问题!
extension-provider
跟着源码的README,安装插件后,运行代码发现报了一堆错误:
|
|
经过Google才知道,因为我们用的是Webpack5,而Webpack5升级之后,相较于Webpack5,移除了很多内置的无用的库,如报错中的util
、path
、os
等等,那怎么解决呢?
在stackoverflow上找到了答案:https://stackoverflow.com/a/65556946/9336091:
也就是通过node-polyfill-webpack-plugin
插件去兼容webpack5升级后带来的问题。
安装完成后,不报错了,但是这个库只给我们提供了一个provider,并没有帮我们注入到环境变量中window.ethereum
:
https://github.com/MetaMask/providers
最终还是绕回到@metamask/providers
中,根据readme操作,发现使用到了LocalMessageDuplexStream
:
Google查找了一下发现这个库来自post-message-stream
因此引入后继续使用,发现window.ethereum
确实有了,但是chainId
为空:
继续思考,可能是provider提供的不正确,还是得从initializeProvider
源码看一下,所以又返回https://github.com/MetaMask/providers查看了一下:
原来实际使用的是MetaMaskInpageProvider
,而之前我们看extension-provider时,已经见过这个provider了:
因此,参照extension-provider
代码,我们使用了MetaMask提供的provider
需要注意的是,这里需要连接浏览器的MetaMask插件: chrome.runtime.connect('nkbihfbeogaeaoehlefnkodbefgpgknn')
,connect里面用到的就是MetaMask在chrome浏览器中的插件id。到这里位置,看起来就合理了。在浏览器里试一下,发现确实提供出了MetaMaskInpageProvider
,而且也有了chainId。
此时,测试发现可以成功调用MetaMask插件了,大功告成~~~
最后
最终通过阅读6~7个库的源码才解决了这个问题,但是还是希望MeatMask官方能解决一下无法在其他浏览器插件中注入window.ethereum
的问题。毕竟也有其他人在这么使用。
最后,来两个推荐:
-
如果想要开发自己的浏览器插件,可以使用https://github.com/lxieyang/chrome-extension-boilerplate-react.git这个模板。
-
如果希望开发自己的DApp应用,可以使用https://usedapp.io/这个工具~~~
如果你觉得这篇文章对你有所帮助,欢迎赞赏~
赞赏