MetaMask API详解
如果你计划将身份验证和身份集成到你的 Web3 应用程序中,那么熟悉 MetaMask API 是开始学习使用 MetaMask 扩展所提供的功能以及了解 Web3技术栈的好方法。
一键发币: 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
:以十六进制形式返回当前链 IDwallet_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_addEthereumChain
和 wallet_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翻译整理,转载请标明出处