MetaMask API详解

如果你计划将身份验证和身份集成到你的 Web3 应用程序中,那么熟悉 MetaMask API 是开始学习使用 MetaMask 扩展所提供的功能以及了解 Web3技术栈的好方法。

MetaMask API详解
一键发币: SOL | BNB | ETH | BASE | Blast | ARB | OP | POLYGON | AVAX | FTM | OK

如果你计划将身份验证和身份集成到你的 Web3 应用程序中,那么熟悉 MetaMask API 是开始学习使用 MetaMask 扩展所提供的功能以及了解 Web3 技术栈的好方法。

1、什么是 MetaMask API?

每当我们使用 Chrome、Firefox 或 Brave 等浏览器并且安装了 MetaMask 等钱包扩展时,我们就可以使用注入到浏览器页面中的以太坊提供程序(由 EIP-1193 指定) window.ethereum 。 从历史上看, Provider 的实现在钱包之间表现出冲突的接口和行为。 该 EIP 正式化了以太坊提供商 API,以促进钱包互操作性。

我们将此提供商与我们的 dapp 结合使用来请求用户的以太坊帐户、读取链上数据并让用户签署消息和交易。

MetaMask API 调用是使用 JSON-RPC 规范定义的,我们将进一步解释这一点。

我们可以在开发者工具中知道MetaMask何时安装并通过开发者工具启用,使用chrome你可以查看 Sources/Page,你会看到一个代表MetaMask的图标。

‌                                                                       Upload

页面用于查看当前网页上的可用资源。在顶层下,我们看到主文档及其所有资源的表示。

如果我们在这里看到由云图标表示的 MetaMask,我们就知道 MetaMask 已安装且未禁用。

window.ethereum 对象被注入到页面中,允许我们与 MetaMask 和以太坊交互。

我们可以在开发者工具控制台中直接访问和调用 window.ethereum对象。 假设我们想要获取当前的 chainId

‌                                                                       Upload

通过在控制台中运行上面的代码,如果我们当前连接到 Polygon,我们将看到此方法调用返回“0x89”。

2、什么是 JSON RPC?

JSON-RPC 是一种与传输无关的 RPC,使用 JSON 数据格式。 当与 JSON-RPC 服务器通信时,无论该服务器是如何构建的或者使用什么语言或平台构建的,您都会发送轻量级 JSON。 它的设计很简单,并且使用正确的工具也相对容易学习。

这很方便,因为这意味着无论我们是通过 HTTP 还是 Web Sockets 发送,我们总是发送所有开发人员都可以访问的 JSON。

JSON RPC 恕我直言,是第一次进入 Web3 并学习在 Dapp 层上构建时需要学习的一项非常重要的技术,我认为你应该熟悉的其他一些技术是:

  • JavaScript 和 TypeScript
  • 对现有的 Web2 概念有很好的理解
  • 智能合约(Solidity

你可以在 jsonrpc.org/specation 了解有关 JSON RPC 规范的更多信息。

3、JSON RPC 请求和响应示例

这里我们有一个典型的 JSON 格式的请求和响应。 第一个示例使用 EIP-695 中概述的 eth_chainId RPC API 方法,该方法返回十六进制格式的整数值的单个 STRING 结果,描述当前配置的用于签署 EIP-155 引入的重放保护交易的 CHAIN_ID 值。

请求:我们在MetaMask中调用 eth_chainId方法,该方法不需要输入任何参数。 请求就像函数调用一样,采用零个或多个参数,我们可以根据这些参数对结果响应进行操作。

‌                   { "id": 0, "jsonrpc": "2.0", "method": "eth_chainId", "params": [] }

id 是唯一的 ,因此客户端可以将响应与其原始请求相关联。就像在 http 上下文中使用 Web 套接字时,这不太相关,但对于批处理请求来说,这是必不可少的。

jsonrpc 字段表明你正在使用的 JSONRPC 版本。

响应结果如下:

‌                   { "jsonrpc": "2.0", "result": "0x1", "id": 0 }

它会在结果字段中返回用户连接到的当前链。 我们可以确定这是主网,因为数字 1 在十六进制值中确实很容易读取,但以十六进制值返回的其他 chainId 更难以读取。 在下面的 JavaScript 示例中,我们将致力于将该十六进制值转换为数字。

JSON RPC 很简单。 上面,我们调用 MetaMask API 方法,但你也可以调用自定义 RPC 端点。 在下一个示例中,我们将研究一个仅返回链列表的自定义 RPC 端点。

再看一个请求:

‌                   { "id": 0, "jsonrpc": "2.0", "method": "list_chains", "params": [1] }

它需要一个参数来表示我们想要返回的链的数量。 为简单起见,我们只返回一个。

上述请求的响应结果如下:

‌                   { "id": 0 "jsonrpc": "2.0", "result": [{ "chain": "Ethereum Mainnet", "chainId": "0x1" }] }

请注意,响应实际上是一个链数组。

在 JavaScript 中工作时,可以使用 Dapp 中的 window.ethereum 对象轻松调用 RPC 端点,该对象连接到 MetaMask 中的 RPC 提供程序。

正如你在这里看到的,我们只是将该请求设置为一个对象,并调用传递 MetaMask API 方法 eth_chainId 的请求方法。

‌                   const chainIdHex = await window.ethereum.request({ "method": "eth_chainId", "params": [] }) console.log(chainIdHex) // 0x1 let chainIdNumber = parseInt(chainIdHex, 16) console.log(chainIdNumber) // 1

我们再次注意到,我们以十六进制值的形式返回了 chainId。 在此示例中, chainId 变量包含十六进制格式的以太坊链 ID 的字符串表示形式(前缀为 0x)。 parseInt 函数用于通过指定基数为 16(十六进制)将此字符串转换为整数。 这将返回以太坊链 ID 的数值,在本例中为 1。

请注意,这只是一个示例,确切的实现将取决于特定的上下文和用例。 您可能需要处理错误和异常,例如当输入字符串不是有效的十六进制数字时,或者当输入不是有效的以太坊链 ID 时。

4、什么是OpenRPC?

OpenRPC 规范为 JSON-RPC 2.0 API 定义了与编程语言无关的标准接口描述。 JSON-RPC API 的 ADL(API 描述语言)

OpenRPC 规范:

  • 与语言无关的界面描述
  • 无需源代码或文档即可发现服务的功能
  • 能够通过最少的实现逻辑与远程服务交互
  • 类似于较低级别编程语言的接口描述
  • 消除调用 JSON-RPC 服务时的猜测

将 OpenRPC 视为 Open API/Swagger,但适用于 JSON-RPC API(它是一个规范/工具)

它是一种人类可读的能力交换格式。 换句话说,它允许人类和计算机发现和理解服务的功能,而无需访问其源代码、文档或检查网络流量。

OpenRPC 提供了一种标准化的方法来描述 API 的结构和参数,以及它接受和返回的数据的类型和格式。 这使得开发人员能够创建其他开发人员可以轻松理解和集成的 API,而无需花费时间和精力来学习每个单独 API 的详细信息。

例如,如果开发人员想要创建一个允许用户与以太坊区块链交互的 API,他们可以使用 OpenRPC 以与其他以太坊相关 API 一致的方式定义 API。 这使得其他开发人员更容易理解和使用该 API,并允许他们将其集成到自己的应用程序中,而无需花时间学习 API 实现的细节。

这是一个高层次的概述,但如果想深入了解 OpenRPC,我建议你查看 2022 年 API 规范会议上 ZaneStarr 的 OpenRPC 简介

5、必备工具

本节重点介绍一些可以帮助处理以下问题的工具:

  • 测试 JSON RPC 服务器
  • 创建Open RPC 规范
  • 使用 MetaMask API
  • 使用智能合约与 MetaMask 交互

当使用 MetaMask 构建时,注入的 window.ethereum 对象的一个很好的资源是我们相应的以太坊提供商 API 文档。

想了解 MetaMask 支持的所有 eth_wallet_ 方法吗?

5.1 以太坊提供商

点击这里查看Mask的以太坊提供商文档。

检查相应的 RPC API 文档,其中包含我们从 JS 代码调用的方法的信息,例如 eth_requestAccounts、`wallet_addEthereumChain` 或 wallet_switchEthereumChain。 也许你需要向 MetaMask 添加一个网络/链,然后将你的用户切换到该链,可以在上面链接出找到文档和代码来演示如何执行此操作:

5.2 MetaMask RPC API

点击这里查看MetaMask RPC API文档。

下一个链接将允许你连接钱包并在浏览器中尝试这些 RPC API 调用,我们计划将 Playground 与我们文档的未来版本合并。

5.3 MetaMask API 游乐场

点击这里查看MetaMask API 游乐场文档。

Chainlink 维护着一个名为 chainlist.org 的网站,它是网络/链信息的重要资源。 我经常浏览该网站,只是寻找各种以太坊网络和测试网、它们的 chainId 以及连接到它们所需的其他信息。 此信息还有其他来源,但这是一个很好的技巧,可以快速查找每个链的信息和 RPC 服务器地址以及有关其延迟和隐私的信息。

当弹出 MetaMask 确认时,你可以添加并切换到列表中的任何链,可以看到将该链添加到 MetaMask 所需的信息。

5.4 Chainlist

点击这里访问Chainlist网站。

下面是使用 Chainlist 的示例,我可以看到 Fantom Opera(主网),它的 chainId,我可以添加到 MetaMask,并在确认中查看用于将网络添加到我的钱包的信息。

5.5 Eserialize

点击这里查看Eserialize网站。

Eserialize 是一个工具,可以将十六进制序列化和反序列化为字符串,或将数字序列化为十六进制,或者反之亦然,下面是如何使用它进行简单操作的示例,例如验证 chainId 的十六进制字符串与 chainlist.org 上列出的数

6、使用示例

MetaMask Playground 列出了我们在 API 中支持的 JSON-RPC 方法。

大多数方法都以 wallet_eth_ 为前缀。

一些更流行和 IMO 最常用的方法是:

  • eth_accounts :获取账户和钱包地址
  • eth_chainId :以十六进制形式返回当前链 ID
  • wallet_addEthereumChain :添加以太坊链
  • wallet_switchEthereumChain :将用户切换到正确的链
  • Contract.mint :调用智能合约函数,在区块链上启动交易,为用户触发 MetaMask

以下示例使用 MetaMask API Playground

当展开每个方法时,你将获得有关参数和结果的信息,并且可以在浏览器中检查和运行每个方法示例。

这里我们测试 eth_accounts 方法来获取连接的帐户:

接下来是调用 eth_chainId 的示例,它将返回我们连接到的网络的当前 chainId

另一种广泛使用的方法是 wallet_addEthereumChain ,这允许 dapp 建议将链添加到用户的钱包中。

只需指定一个 chainId 和一些链元数据。 钱包应用程序可以任意拒绝或接受该请求。 如果添加了链,则返回 null 值,否则返回错误。

该 API 端点由 EIP 3085 引入。

以下是用户添加链时看到的内容:

下一个 API 方法切换当前激活的链。 由 EIP 3326 引入。

用户将看到类似于以下的对话:

注意:如果你尝试切换的链尚未添加到用户钱包中,则此方法将引发错误。

因此,当我们想要将用户切换到Dapp中的另一个链时,我们可以将 wallet_addEthereumChainwallet_switchEthereumChain方法组合在 try / catch中。

让我们看一下如何在 React 组件中添加/切换以太坊链到 Polygon Mainnet:

import { useContext } from 'react'
import { MetaMaskContext } from '../../../context/MetaMaskProvider'
import MyButton from '../atoms/MyButton'

const ConnectNetwork = () => {
  const { provider } = useContext(MetaMaskContext)

  const addSwitchNetwork = async () => {
    if (provider) {
      try {
        await window.ethereum.request({
          method: 'wallet_switchEthereumChain',
          params: [{ chainId: '0x4' }],
        })
      } catch (error) {
        try {
          await window.ethereum.request({
            method: 'wallet_addEthereumChain',
            params: [
              {
                chainId: '0x313337',
                blockExplorerUrls: ['https://polygonscan.com/'],
                chainName: 'Polygon Mainnet',
                nativeCurrency: {
                  decimals: 18,
                  name: 'Polygon',
                  symbol: 'MATIC'
                },
                rpcUrls: ['https://polygon-rpc.com']
              },
            ],
          })
        } catch (error) {
          // user rejects the request to "add chain" or param values are wrong, maybe you didn't use hex above for `chainId`?
          console.log(`wallet_addEthereumChain Error: ${error.message}`)
        }
      }
    }
  }

  return (
    <MyButton handleClick={addSwitchNetwork}>
      <p>Connect Rinkeby</p>
    </MyButton>
  )
}

export default ConnectNetwork

上面的代码假设你正在使用某种类型的 Context API 组件来跟踪钱包状态并将其提供给应用程序中的组件,但如果不使用 React,你可以专注于代码的 try/catch 部分并修改它以适合你的应用程序。

7、使用 SDK 连接到 MetaMask Mobile

如果想让你的用户轻松连接到 MetaMask 扩展或 MetaMask Mobile(从各种平台),你应该考虑使用 MetaMask SDK,你可以在其中了解更多信息并链接到文档。 我们在 React 和 NextJS 中有示例应用程序。 MetaMask SDK 在幕后利用 MetaMask API,尽管这是另一个主题,但值得在这里介绍一下。

有关如何将 MetaMask SDK 集成到 NextJS 应用程序中的示例,请查看我们的入门示例

7.1 智能合约示例

如果我们想调用具有 payable 关键字的智能合约函数来接收以太币,下面是如何从智能合约触发 MetaMask 的示例:

contract MyNFT {
  uint256 public totalSupply;
  uint256 public maxSupply;
  mapping(uint256 => address) public tokenOwners;

  function mint(uint256 tokenId) public payable {
    require(totalSupply + 1 <= maxSupply, "Cannot mint more tokens: maximum supply reached");
    require(tokenOwners[tokenId] == address(0), "Token ID already in use");
    tokenOwners[tokenId] = msg.sender;
    totalSupply++;
  }
}

添加 payable 关键字后,可以通过包含付款的交易来调用此 mint() 函数。 随交易发送的以太币数量将作为 msg.value 参数提供给函数。 请注意,你可能还需要向函数添加额外的逻辑来处理收到的以太币,例如将其转移到合约所有者的帐户。

该函数采用 uint256 参数,表示要铸造的 NFT 的唯一代币 ID。 该函数首先检查铸造新代币后 totalSupply是否大于 maxSupply。 如果是这种情况,该函数将恢复并显示错误消息。 然后该函数检查指定的代币ID 是否已在使用中。 如果代币 ID 已被使用,该函数将恢复并显示错误消息。 否则,该函数将更新代币所有权映射,以将代币分配给函数的调用者(使用 msg.sender 变量)并增加 totalSupply

然后,我们可以通过 React 组件在智能合约中调用此函数,如下所示:

const MintingPage = ({nft}) => {

  const [isMinting, setIsMinting] = useState(false)
  const [error, setError] = useState(false)
  const [errorMessage, setErrorMessage] = useState("")
  const { user, nftContract, chainId } = useContext(MetaMaskContext)
  const { address } = user

  const mintNFT = async () => {
    console.log("start minting")
    setIsMinting(true)

    nftContract.mint({
      from: address,
      value: nft.priceHexValue
    })
    .then(async(tx) => {
      await tx.await()
      console.lgo(`minting complete, mined: ${tx}`)
      setIsMinting(false)
    })
    .catch((error) => {
      console.log(error)
      setError(true)
      setErrorMessage(error?.message)
      setIsMinting(false)
    })
  }
}

我们使用 await关键字等待区块链上的交易被挖掘,然后再继续执行代码。

.then.catch 语法用于处理事务的结果。 如果交易成功, .then 块将被执行。 如果事务失败, .catch 块将被执行,并且组件的状态将使用错误消息进行更新。

mint 函数调用合约上的 mint 方法,然后使用 wait 方法等待交易被挖掘。 这将确保铸币功能不会继续执行,直到交易在区块链上成功开采。 这对于确保在其余代码继续执行之前更新合约的状态非常有用。


原文链接:Introduction to MetaMask API

DefiPlot翻译整理,转载请标明出处

通过 NowPayments 打赏