我们在第三讲连接钱包中介绍了如何连接钱包,但是没有深入讲解连接钱包的原理。EIP1193 和 EIP6963 是两个重要的协议,它们定义了 DApp 连接钱包的标准。EIP1193 是最早期的标准,但是有一些缺陷,EIP6963 则是对 EIP1193 的改进,解决了那些缺陷。
本文将介绍这两个协议的基本概念,帮助你理解链接钱包的逻辑。
EIP1193 的规范地址在https://eips.ethereum.org/EIPS/eip-1193,它定义了在浏览器中如何通过 JavaScript 与钱包进行交互,有了这个规范钱包才能按照他提供相关接口,DApp 才能按照它调用钱包提供的接口。
这个定义很简单,其实就是约定了浏览器运行时的全局对象 window
上的 ethereum
对象的格式,定义了一些方法和事件。
对于 DApp 来说,它需要检查 window.ethereum
是否存在,如果存在,那么它就可以调用 window.ethereum
上的方法来和钱包进行交互。就同调用浏览器其他 API 类似,比如 window.localStorage
。
下面是一个简单的例子可以获取链的 ID:
const ethereum = window.ethereum;
ethereum
.request({ method: "eth_chainId" })
.then((chainId) => {
console.log(`hexadecimal string: ${chainId}`);
console.log(`decimal number: ${parseInt(chainId, 16)}`);
})
.catch((error) => {
console.error(`Error fetching chainId: ${error.code}: ${error.message}`);
});
你可以尝试在浏览器的控制台运行查看效果:
对于更多方法,也就对应修改 request
调用时的参数即可,支持的方法可以参考 JSON-RPC API。当然,对于一些钱包可能不支持的方法,你需要做好异常处理。也有一些钱包特有的一些方法或者一些已经约定俗成的方法,你需要查看钱包的文档。
通常来说,在 DApp 中你应该使用类似 web3.js
、ethers
、viem
这样的 SDK 来和钱包进行交互,这些 SDK 会帮你封装好一些方法,让你更方便的和钱包进行交互。
以上就是 EIP1193 的基本概念,但是 EIP1193 有一个主要的缺陷。就是 window.ethereum
对象只有一个,所以当用户安装了多个钱包时,用户只能选择一个钱包来使用。这样会导致钱包之间会争抢 window.ethereum
对象,一方面损害了用户体验,另外也不利于钱包之间的良性竞争。
在之前很长一段时间,针对这个问题钱包的做法一般是会注入自己独特的对象,比如 TokenPocket 会注入 window.tokenPocket
。但是这样的做法并不是标准的,也不是一个好的解决方案。另外,这样的做法也会导致 DApp 需要适配很多钱包,增加了 DApp 的开发成本。
所以就有了 EIP6963,接下来我们会介绍 EIP6963。
EIP6963 的规范地址在https://eips.ethereum.org/EIPS/eip-6963。
EIP6963 不再通过 window.ethereum
对象来和钱包进行交互,而是通过发送往 window
发送事件的方式来和钱包进行交互。这样就解决了 EIP1193 的问题,多个钱包可以和 DApp 进行交互,而不会争抢 window.ethereum
对象。
另外钱包也可以通过发送事件的方式主动告知 DApp 它的存在,这样 DApp 就可以知道用户安装了哪些钱包,然后根据用户的选择来和钱包进行交互。
技术上来讲其实就是通过浏览器的 window.addEventListener
来监听消息,通过 window.dispatchEvent
来发送消息。所有的消息的 type
都有 eip6963:
前缀,具体的消息内容定义可以参考规范文档。
对于开发者来说,和 EIP1193 一样,你使用一些社区的库即可,这样可以免去对细节的关注。比如你如果使用 wagmi,那么通过配置 multiInjectedProviderDiscovery 即可接入 EIP6963。
如果你使用了 Ant Design Web3,通过配置 WagmiWeb3ConfigProvider
的 eip6963
即可在 DApp 中使用 EIP6963。它的连接钱包的弹窗会自动添加检测到的钱包。
下面是我们基于之前的课程例子的修改示例:
export default function Web3() {
return (
<WagmiWeb3ConfigProvider
config={config}
wallets={[MetaMask()]}
+ eip6963={{
+ autoAddInjectedWallets: true,
+ }}
>
<Address format address="0xEcd0D12E21805803f70de03B72B1C162dB0898d9" />
<NFTCard
address="0xEcd0D12E21805803f70de03B72B1C162dB0898d9"
tokenId={641}
/>
<Connector>
<ConnectButton />
</Connector>
<CallTest />
</WagmiWeb3ConfigProvider>
);
}
其中配置了 eip6963
使得使用通过 EIP6963 协议连接钱包,避免了多个钱包之间可能出现的冲突。另外添加了 autoAddInjectedWallets
配置使得自动添加检测到的钱包到 Ant Design Web3 的 UI 中,提升用户体验,让用户可以自由选择他已经安装的钱包。
不管是 EIP1193 还是 EIP6963,它们都是通过浏览器的 JavaScript API 来和钱包进行交互的。它要求钱包可以向 DApp 的运行时注入对象或者发送事件,比如通过 Chrome 浏览器插件,或者你在钱包内置的浏览器中访问 DApp。
但是对于有的场景,用户没有安装插件,或者是在移动端浏览器访问 DApp,无法使用插件。又或者用户需要用其他手机安装的钱包客户端来连接 DApp。不管是 EIP1193 还是 EIP6963,都无法满足这些场景。所以,我们还需要其他的方式来连接钱包,比如 WalletConnect。我们会在下一讲介绍如何使用 WalletConnect 来连接钱包。