Tenderly Simulate Asset Changes Snap
Tenderly MetaMask Snap 演示
项目概览
本项目向您展示如何将交易模拟集成到 MetaMask 钱包中。我们将使用 MetaMask Snaps。 您将学习如何连接到 Tenderly Simulation API、发送交易数据、获取模拟结果,并通过 MetaMask UI 向用户展示这些结果。这包括以美元表示的资产变化、原生资产余额变化、输出值、存储变化、事件日志以及调用追踪。 模拟后的交易随后还可以在 Tenderly 中打开,以进行进一步的调试和测试。 以下是我们将添加到 MetaMask Snap 的功能列表。类似的功能也可以添加到任何钱包中,帮助用户在发送交易时建立信心。| 功能 | 描述 |
|---|---|
| 模拟调试 | 自动生成的链接,用于在 Tenderly 中检查和调试该模拟。 |
| 可公开分享的模拟 | 能够启用/禁用模拟的公开分享。 |
| 以美元价值显示的资产变化 | 人类可读的数据,包括自动转换为美元的资产变化。 |
| 原生资产余额变化 | 能够跟踪合约执行期间原生资产余额的变化。 |
| 输出值 | 查看合约调用的结果,显示合约将要返回的内容。 |
| 存储变化 | 查看合约执行期间对存储所做的任何更改。 |
| 事件日志 | 查看在合约执行过程中发出的事件。 |
| 调用追踪 | 执行期间发生的所有函数调用的分解。 |
前置条件
- 已安装 MetaMask Flask 浏览器扩展
- Node.js(版本 16 或更高)
- Yarn(版本 3)
在开始构建 MetaMask Snap 之前,请务必禁用”正式版”的 MetaMask,并
安装 MetaMask Flask 开发
插件。
第 1 步:设置 MetaMask Snap 项目
安装 MetaMask 官方的 Create Snap CLI 并创建一个新项目。example
example
example
设置正确的权限
安装 Snap 后,找到/packages/snap/snap.manifest.json 文件,并按下方所示设置权限。在这里了解 MetaMask 权限。
example.json
.webp?fit=max&auto=format&n=XsEZlaGXYskrtN68&q=85&s=fe280e95a931009bdc123e0018703f04)
第 2 步:生成 Tenderly API 访问令牌
MetaMask Snap 中的交易预览选项由 Tenderly Simulation API 提供支持。 在开始使用 API 之前,您需要生成访问令牌。请按以下步骤操作:- 登录 Tenderly 或在此免费创建一个账户。
- 前往 Authorization 页面并点击 Generate Access Token 按钮。
如何生成 API 访问令牌
.webp?fit=max&auto=format&n=XsEZlaGXYskrtN68&q=85&s=605d334b40293766a111dd1651f30d92)
第 3 步:编写 Snap 的逻辑
使 Snap 正常工作的核心逻辑存储在三个文件中:- 凭据访问(
credentials-access.ts):管理 Tenderly API 的身份认证和访问。 - 模拟(
simulation.ts):与 Tenderly API 交互以模拟交易并获取模拟结果的核心逻辑。 - 格式化器(
formatter.ts):解析模拟结果并以对用户友好的方式在 MetaMask UI 中格式化它们的逻辑。
凭据访问
我们可以将所有负责请求、更新和获取 Tenderly API 凭据的方法存储在一个名为 credentials-access.ts 的文件中。在此查看源代码。 最重要的方法包括:fetchCredentials():此函数获取 Tenderly 项目的凭据。如果没有存储凭据,则触发handleUpdateTenderlyCredentials请求新的凭据。handleUpdateTenderlyCredentials():此函数处理更新 Tenderly 项目凭据的流程。它通过调用requestNewTenderlyCredentials获取新凭据,然后使用带有'update'操作的snap_manageState方法保存它们。requestNewTenderlyCredentials():此函数请求新的 Tenderly 凭据。它调用requestCredentials以接收原始凭据数据,并在返回带有accountId、projectId和accessToken的对象之前验证其正确性。
snap_manageState 是一个内置方法,允许 Snap 将最多 100 MB 的数据持久化到
磁盘。在这里了解更多。
模拟
接下来,我们需要创建将交易数据发送到 Tenderly Simulation API、进行模拟并获取结果的逻辑。 我们将使用simulate API 端点。
端点:https://api.tenderly.co/api/v1/account/{accountId}/project/{projectId}/simulate
为了保持代码结构清晰,我们将模拟逻辑存储在一个名为 simulation.ts 的文件中。在此查看源代码。
要向 Tenderly Simulation API 发送交易数据,我们需要创建两个关键方法:
- simulate():这是处理交易模拟的主函数。它获取 API 访问凭据、将交易数据提交给 Tenderly Simulation API,并处理 API 返回的任何错误。
example.tsx
- submitSimulation():此函数向 API 发送带有要模拟的交易数据的请求。
example.tsx
格式化器
一旦 API 返回模拟结果,我们需要解析这些数据,并以对用户友好的方式格式化,以便在 MetaMask UI 中显示。 我们可以将此逻辑存储在一个名为 formatter.ts 的文件中。在此查看源代码。 核心格式化方法包括:formatResponse():此函数接收 Tenderly 项目模拟的原始数据和凭据,调用每个相关部分(如余额变化、输出值、资产变化等)的单个格式化函数,并返回一个包含所有格式化输出的面板。formatBalanceDiff():此函数生成一个面板,显示交易中涉及的每个账户的余额变化。formatOutputValue():如果存在输出值,此函数会创建一个面板来展示交易的输出值。如果可能,它还会解码输出。formatAssetChanges():此函数格式化一个面板以显示任何资产变化,区分 ERC20、ERC721 及其他变化。formatStorageChanges():此函数创建一个面板,列出交易期间对存储的任何更改。为了清晰起见,它对地址和嵌套数据结构进行了独特的格式化。formatEventLogs():如果存在事件日志,此函数会为每笔交易展示它们。日志被格式化以增强可读性,并包含输入值。formatCallTrace():此函数生成调用追踪的可视化层级结构,递归显示嵌套调用。formatSimulationUrl():此函数返回一个指向 Tenderly Dashboard 上模拟完整详情的链接,以及一个单独的可分享链接。
第 4 步:在 MetaMask UI 中显示模拟数据
MetaMask Snap 单体仓库提供了一组预定义的 UI 元素和布局配置。我们将使用这些元素扩展现有的 MetaMask UI,以显示模拟结果。 当您安装我们的 MetaMask 示例后,我们将向您展示如何运行不同类型的模拟:- 使用预定义负载的成功交易模拟(成功的 ERC-20 转账和 NFT 转账)
- 使用预定义负载的失败交易模拟
- 使用任意负载的自定义交易模拟
%20(1).webp?fit=max&auto=format&n=XsEZlaGXYskrtN68&q=85&s=c6f3c33067b72e0ac097fac8095498b0)
使用预定义负载的成功模拟
要启动模拟,我们编写一个函数,从我们的钱包地址发送预定义的交易数据,并调用eth_sendTransaction RPC 方法。
example.tsx
eth_sendTransaction 方法将触发 onTransaction 处理器,进而调用我们在 simulation.ts 中定义的自定义 simulate 函数。
example.tsx
onTransaction 处理器中,simulate 函数以交易及其来源作为参数被调用。
simulate 函数负责获取访问凭据、将交易数据提交给 Tenderly API、处理任何错误,以及格式化 API 响应以便输出。
模拟结果显示在 MetaMask UI 中。下方图片展示了一次将 1 USDC 成功转账给 demo.eth 的 ERC20 代币转账。该输出由 formatter.ts 文件中定义的函数生成。
.webp?fit=max&auto=format&n=XsEZlaGXYskrtN68&q=85&s=bb92ed11d3534aae5065bb162b2d2a83)
formatAssetChanges() 函数生成。
您还会获得一个指向该模拟交易的链接,可以在 Tenderly 中打开它,进一步使用 Debugger 检查它,或使用不同的值重新模拟。
此链接由 formatter.ts 文件中定义的 formatSimulationUrl() 函数生成。
example.ts
使用预定义负载的失败交易
在第二个示例中,我们将使用预定义负载模拟一笔失败的交易。 下方图片显示了一次将 1,000,000 USDC 转账给demo.eth 的失败 ERC20 代币转账。
.webp?fit=max&auto=format&n=XsEZlaGXYskrtN68&q=85&s=aba83c7ebdcd5583f17f53c9b21fe306)
使用自定义负载的模拟
要使用自定义负载运行模拟,您需要添加一个符合 Ethereum 交易规范的对象。.webp?fit=max&auto=format&n=XsEZlaGXYskrtN68&q=85&s=3910b1ef37fc7a16edfc5718e7446df5)
from:DATA,20 字节 - 交易的发送方地址。to:DATA,20 字节 -(创建新合约时可选)交易的目标地址。gas:QUANTITY-(可选,默认:90000)为交易执行提供的 gas 的整数。它将返回未使用的 gas。gasPrice:QUANTITY-(可选,默认:待定)每单位已支付 gas 所用的gasPrice整数。value:QUANTITY-(可选)随此交易发送的 value 整数。data:DATA- 合约的编译代码,或已调用方法签名和已编码参数的哈希。nonce:QUANTITY-(可选)nonce 整数。这允许您覆盖使用相同 nonce 的自己的待处理交易。
.webp?fit=max&auto=format&n=XsEZlaGXYskrtN68&q=85&s=6544e62caa22bfd1c5affec7c1f52792)
sendTransaction 函数发起模拟。此操作会将自定义负载传递给 onTransaction 处理器,进而触发 simulate 函数运行模拟。