目录

在自己开发的插件中调用MetaMask插件来实现以太坊账户登录和交互

背景

最近想制作一个浏览器新标签页导航器(类似于infinitytab)的chrome插件,其中,希望在插件中调用MetaMask来作为账户登录。

在开发插件时,遇到了一个致命的问题!我们都知道,如果浏览器安装了MetaMask插件, 那么会默认在浏览器window环境上注入window.ethereum,如下图所示

https://hicoldcat.oss-cn-hangzhou.aliyuncs.com/img/202207141848816.png

这个是MetaMask提供的默认Provider,可以访问以太坊账户的信息。

但是,在新标签页,MetaMask是不注入这个变量的,也就是在chrome-extension://这个地址下面,是没有注入Provider的。

https://hicoldcat.oss-cn-hangzhou.aliyuncs.com/img/202207141851489.png

这就导致了当我们自己开发了一个新标签工具,想要直接调用MetaMask的账户登录时,是没什么反应的。

怎么解决?

window.ethereum 如何注入浏览器的?

首先想到的解决方法,试看一下MetaMask是怎么注入到浏览器中这个window.ethereum变量的。因此,我找到了 MetaMask浏览器插件的源码:https://github.com/MetaMask/metamask-extension

metamask-extension

在这个插件中,我先按照window.ethereum搜索了一下,没有任何的发现:

https://hicoldcat.oss-cn-hangzhou.aliyuncs.com/img/202207141902598.png

于是,开始寻找是否给页面注入了js脚本,最终在app/scripts/contentscript.js文件中看到了injectScript函数:

https://hicoldcat.oss-cn-hangzhou.aliyuncs.com/img/202207141906153.png

从这个文件我们看到使用到了inpage.js文件,于是在app/scripts/inpage.js文件,看到了initializeProvider函数,猜测大概率跟这个函数有关。而这个函数是从@metamask/providers/dist/initializeInpageProvider中导入的,于是,我找到了@metamask/providers包的源码。

https://github.com/MetaMask/providers

在这个包中,找到了initializeInpageProvider的源码,其中用到了setGlobalProvider函数

https://hicoldcat.oss-cn-hangzhou.aliyuncs.com/img/202207141911352.png

在这个文件的下方我们找到了这个setGlobalProvider函数,

https://hicoldcat.oss-cn-hangzhou.aliyuncs.com/img/202207141913280.png

终于,原来是在这里,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

https://hicoldcat.oss-cn-hangzhou.aliyuncs.com/img/202207141856708.png

意思是说他们有个extension-provider的库也许可以解决这个问题。

那么,就继续解决问题!

extension-provider

跟着源码的README,安装插件后,运行代码发现报了一堆错误:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
ERROR in ./node_modules/post-message-stream/index.js 2:17-41
Module not found: Error: Can't resolve 'util' in '/Users/mac/Mine/tabverse/node_modules/post-message-stream'

BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.

If you want to include a polyfill, you need to:
	- add a fallback 'resolve.fallback: { "util": require.resolve("util/") }'
	- install 'util'
If you don't want to include a polyfill, you can use an empty module like this:
	resolve.fallback: { "util": false }

经过Google才知道,因为我们用的是Webpack5,而Webpack5升级之后,相较于Webpack5,移除了很多内置的无用的库,如报错中的utilpathos等等,那怎么解决呢?

在stackoverflow上找到了答案:https://stackoverflow.com/a/65556946/9336091:

https://hicoldcat.oss-cn-hangzhou.aliyuncs.com/img/202207141923964.png

也就是通过node-polyfill-webpack-plugin插件去兼容webpack5升级后带来的问题。

安装完成后,不报错了,但是这个库只给我们提供了一个provider,并没有帮我们注入到环境变量中window.ethereum

https://hicoldcat.oss-cn-hangzhou.aliyuncs.com/img/202207141937426.png

https://github.com/MetaMask/providers

最终还是绕回到@metamask/providers中,根据readme操作,发现使用到了LocalMessageDuplexStream:

https://hicoldcat.oss-cn-hangzhou.aliyuncs.com/img/202207141941176.png

Google查找了一下发现这个库来自post-message-stream

https://hicoldcat.oss-cn-hangzhou.aliyuncs.com/img/202207141944115.png

因此引入后继续使用,发现window.ethereum确实有了,但是chainId为空:

https://hicoldcat.oss-cn-hangzhou.aliyuncs.com/img/202207141945498.png

https://hicoldcat.oss-cn-hangzhou.aliyuncs.com/img/202207141946998.png

继续思考,可能是provider提供的不正确,还是得从initializeProvider源码看一下,所以又返回https://github.com/MetaMask/providers查看了一下:

https://hicoldcat.oss-cn-hangzhou.aliyuncs.com/img/202207141949079.png

原来实际使用的是MetaMaskInpageProvider,而之前我们看extension-provider时,已经见过这个provider了:

https://hicoldcat.oss-cn-hangzhou.aliyuncs.com/img/202207141948384.png

因此,参照extension-provider代码,我们使用了MetaMask提供的provider

https://hicoldcat.oss-cn-hangzhou.aliyuncs.com/img/202207141952081.png

需要注意的是,这里需要连接浏览器的MetaMask插件: chrome.runtime.connect('nkbihfbeogaeaoehlefnkodbefgpgknn'),connect里面用到的就是MetaMask在chrome浏览器中的插件id。到这里位置,看起来就合理了。在浏览器里试一下,发现确实提供出了MetaMaskInpageProvider,而且也有了chainId。

https://hicoldcat.oss-cn-hangzhou.aliyuncs.com/img/202207141955981.png

此时,测试发现可以成功调用MetaMask插件了,大功告成~~~

最后

最终通过阅读6~7个库的源码才解决了这个问题,但是还是希望MeatMask官方能解决一下无法在其他浏览器插件中注入window.ethereum的问题。毕竟也有其他人在这么使用。

https://hicoldcat.oss-cn-hangzhou.aliyuncs.com/img/202207142007417.png

最后,来两个推荐:

https://hicoldcat.oss-cn-hangzhou.aliyuncs.com/img/my.png