前言
如果浏览器插件钱包需要向 Dapp 提供以太坊 Provider 能力,必须实现 EIP1193 协议。根据 EIP1193 协议描述,要求插件钱包将 Provider 实现注入到 window.ethereum
。 这就导致了一些问题。
- 注入冲突。用户如果安装了多个插件钱包,就会造成
window.ethereum
被重复覆盖,最终值取决于插件钱包的加载顺序。 - 恶意竞争。为了让自家钱包的 Provider 实现能够成功注入到
window.ethereum
, 有些插件钱包会延迟自家 Provider 的注入,甚至freeze
window.ethereum
对象,使其无法被覆盖。
同时,由于 EIP1193 没有定义钱包 Provider 的元数据描述接口,导致 Dapp 无法统一自动解析 Provider 中的特定信息来区分不同钱包。插件钱包的元数据(图标,名称等)如果要正确被显示,就需要钱包开发者手动将这些信息提交给 Dapp 或者 Provider Discovery Library (https://github.com/rainbow-me/rainbowkit/pull/1166),耗时耗力。
EIP-6963 协议介绍
EIP-6963(Multi Injected Provider Discovery),就很好地解决了上述这些问题。下面简要概述下该协议的核心内容。
EIP6963ProviderInfo
该协议定义了 一个标准化的 EIP6963ProviderInfo
接口,包含了钱包 Provider 的一些相关元数据。
1 | /** |
- uuid: 符合 UUID v4.0 规范的钱包本地唯一标识符。
- name: 人类可读的钱包名称。
- icon: 指向图像的 URI,应为 96x96px 的最小分辨率的正方形。建议使用 PNG , WebP 或SVG。
- rdns: 钱包的反向域名标识符,如
io.metamask.wallet
EIP6963ProviderDetail
该协议定义了一个标准化的 EIP6963ProviderDetail
接口,用于公布钱包 Provider 对象(实现EIP1193规范)和钱包 Provider 的一些相关元数据(实现EIP6963ProviderInfo
接口)。
1 | interface EIP6963ProviderDetail { |
Announce和Request 事件
该协议引入了一组 window 事件,通过事件实现 Dapp 与钱包 Provider 的双向通信。
Dapp 与钱包 Provider 都需要使用
window.dispatchEvent
函数来触发事件,使用window.addEventListener
来监听事件。定义了
EIP6963AnnounceProviderEvent
接口,并且实现该接口的对象必须是CustomEvent
对象。应该使用Object.freeze()
冻结detail
属性。1
2
3
4
5// Announce Event dispatched by a Wallet
interface EIP6963AnnounceProviderEvent extends CustomEvent {
type: "eip6963:announceProvider";
detail: EIP6963ProviderDetail;
}定义了
EIP6963RequestProviderEvent
接口,并且实现该接口的对象必须是Event
对象。1
2
3
4// Request Event dispatched by a DApp
interface EIP6963RequestProviderEvent extends Event {
type: "eip6963:requestProvider";
}
通信流程
钱包端
- 钱包通过
window.addEventListener
监听eip6963:requestProvider
事件, 当收到该事件时,应该立即通过window.dispatchEvent
触发EIP6963AnnounceProviderEvent
事件,将钱包的EIP6963ProviderDetail
信息发送给 Dapp。 - 由于钱包可能晚于 Dapp 加载完成,导致未能成功监听到 Dapp 发送的
eip6963:requestProvider
事件。所以当钱包加载完成时,应该立即通过window.dispatchEvent
触发一次EIP6963AnnounceProviderEvent
。
1 | let info: EIP6963ProviderInfo; |
Dapp端
- Dapp 应该通过
window.addEventListener
监听eip6963:announceProvider
事件。并且在页面生命周期内,不得移除该事件监听。 - 当监听
eip6963:announceProvider
事件后,Dapp 才可以通过window.dispatchEvent
发送eip6963:requestProvider
事件,向钱包请求EIP6963ProviderDetail
信息。
1 | // The DApp listens to announced providers |
EIP-6963 具体实现
- 一些主流钱包已经陆续实现了该协议,如: Metamask, Okx Wallet, Bitget Wallet, Brave, Rainbow, Safeheron Wallet 等。更多钱包见https://github.com/WalletConnect/EIP6963/blob/master/src/utils/constants.ts
- 如果你的插件钱包需要实现该协议,可以参考Metamask代码https://github.com/MetaMask/providers/blob/v14.0.2/src/EIP6963.ts
EIP-6963 测试
如果需要测试钱包是否支持了该协议,可以使用如下 Dapp 测试