第11章 使用原生 JS 连接 MetaMask 构建 dApp
# 你的版本可能不同,但建议使用当前稳定版本
v18.16.1
$: npm -v
9.5.1
请确保已安装 MetaMask 浏览器扩展,并完成创建账户的步骤(例如设置密码、查看种子短语)。 第 2 步:为教程创建新目录 为教程创建一个新目录并进入其中: mkdir web3js-metamask-tutorial
cd web3js-metamask-tutorial
第 3 步:将 MetaMask 用作 Web3.js 的 Provider 在你的项目目录中创建一个名为 index.html 的新文件,并添加以下 HTML 内容:
查看 index.html 中的注释。这个文件引入了 Web3.js,创建了几个用于展示网络信息的占位符,并定义了一段脚本用来检测是否有注入的 provider。如果检测到,就用它构建一个新的 Web3 实例并连接到 Ethereum 网络。脚本会检查该 provider 是否来自 MetaMask,并把结果显示给用户。连接成功后,脚本会将链 ID 和最新区块号填入占位符,并创建事件订阅,在新区块产生时实时更新区块号。如果没有检测到注入的 provider,则提示用户安装 MetaMask。 要在项目目录下启动一个本地 HTTP 服务器来提供 index.html,执行以下命令: npx watch-http-server .
输出应该类似: Websocket Server Listening on Port: 8086
启动 http-server,在 http://0.0.0.0:8080 上提供当前目录的访问。
按 CTRL-C 可停止服务器。
已扫描工作目录,就绪并等待变更……
HTTP 服务器必须保持运行在启动它的那个终端中。对项目目录所做的任何更改都会触发网页自动刷新。 使用支持 MetaMask 的浏览器访问输出中显示的 URL(例如上述示例中的 http://0.0.0.0:8080)。如果配置正确,网页应显示其已通过 MetaMask 连接到 Ethereum 网络,并列出 chain ID(对于默认的 Ethereum 主网,该值应为 1)和最新区块编号。随着新区块的生成,最新区块编号应随之更新。 步骤 4:请求访问 MetaMask 账户 用以下内容替换 index.html 的内容(注意高亮部分为新增内容):
文件已更新,添加了 MetaMask 账户的占位符,以及一个用于向 MetaMask 请求账户的按钮。 连接到 MetaMask 后,脚本会为新按钮注册点击事件。通过调用 window.ethereum.request({ method: ["eth_requestAccounts"] }) 来请求账户,请求获批后,调用 web3.eth.getAccounts() 即可获取账户地址。MetaMask 支持管理多个账户,但本教程只用单个账户。关于 window.ethereum.request 函数和 eth_requestAccounts RPC 调用的更多信息,可参阅 MetaMask 文档。 如果找不到 Ethereum provider,请求 MetaMask 账户的按钮将被禁用。 回到启用了 MetaMask 的浏览器查看网页,页面应已自动刷新并显示最新更改。点击"Request MetaMask Accounts"按钮,这会唤起 MetaMask。接受 MetaMask 的通知后,网页上就会显示 MetaMask 账户的地址。MetaMask 会记住该网页已获得访问其账户的授权,之后再请求账户时就无需再次确认了。 第 5 步:用 MetaMask 账户签名消息 将 index.html 的内容替换为以下代码(注意高亮部分,它们是新增的):
index.html 已更新,新增了一组用于通过 MetaMask 账户签署消息的输入项。起初这些输入项处于禁用状态,只有当页面获取到 MetaMask 账户访问权限后才会启用。同时,代码中添加了一个占位符,用于展示签名操作的结果。 在“Request MetaMask Accounts”按钮的点击事件中,签名相关的输入项得到初始化。首先,启用这些输入项;随后,为“Sign”按钮注册一个点击事件。该事件会调用 web3.eth.personal.sign 方法。该方法接收三个参数:第一个是待签署的消息,取自输入框的内容;第二个是用于签名的账户地址;第三个是解密账户所需的密码短语,由于 MetaMask 正在管理账户,本示例中不需要使用此参数。消息签署完成后,占位符会被更新为签署后的消息内容。 回到已安装 MetaMask 的浏览器并刷新网页,页面应已自动更新以展示上述变化。此时,“Request MetaMask Accounts”按钮下方应出现处于禁用状态的输入框。点击按钮请求访问账户(注意,这次无需再接受 MetaMask 的弹窗通知)。如果一切正常,网页上会显示 MetaMask 账户的地址,且输入框会被启用。在输入框中键入一条消息(例如“Hello, Web3.js!”),然后点击“Sign Message”按钮。若流程正确,系统将弹出一条 MetaMask 通知。接受该通知后,签署后的消息将显示在输入框下方。 第 6 步:验证用于签署消息的账户 用以下内容替换 index.html 中的代码(注意标出的新增部分):
与上一步类似,index.html 已更新,添加了用于恢复签名的账户的输入框。这些输入框目前也是禁用状态——待页面获取到 MetaMask 账户权限后,它们会被启用。此外,还添加了一个占位符,用于显示账户恢复操作的结果。
在“Request MetaMask Accounts”按钮的点击事件中,会对恢复功能相关的输入框进行初始化。首先,启用这些输入框;接着,为“Recover”按钮注册一个点击事件。该事件会调用 web3.eth.personal.ecRecover 方法。此方法的第一个参数是原始未签名的消息,取自第一个输入框;第二个参数是签名后的消息,取自第二个输入框。一旦成功恢复账户,占位符就会更新为该账户的地址。
返回已启用 MetaMask 的浏览器并刷新网页,页面应会自动重新加载以展示变更。此时,在上一步的输入框下方,应能看到新增的处于禁用状态的输入框。按照之前的步骤,从 MetaMask 请求账户权限并签署一条消息,然后将原始消息和消息签名(包含开头的 0x 字符)分别复制粘贴到新的输入框中。点击“Recover Account”按钮。如果一切正常,新输入框下方会显示出用于签署该消息的账户地址。这个地址应与之前签署消息时显示的账户地址一致。
结语
本教程演示了如何使用 Web3.js 配合 MetaMask,包括使用 MetaMask 注入的 Provider,以及使用 MetaMask 账户签署消息。若要在 Web3.js 中使用 MetaMask 注入的 Provider,只需使用 window.ethereum 属性来构造一个 Web3 类的新实例即可。要请求访问 MetaMask 账户,请调用 window.ethereum.request({ method: "eth_requestAccounts" }) —— 当用户确认该请求后,MetaMask 账户便可通过 web3.eth.getAccounts() 获取。当 MetaMask 注入的 Provider 处理需要用到 MetaMask 账户的请求时,MetaMask 会弹窗提示用户确认该请求。