使用 ERC-7730 为你的协议添加清晰签名

Ethereum.org 发布于 2026-05-11 阅读 185

本文教程介绍ERC-7730标准,该标准定义了一种JSON格式,用于描述智能合约函数调用的含义,使钱包在用户签署交易时能够显示人类可读的信息(如“Swap 1000 USDC for at least 0.42 WETH”),从而实现“所见即所签”(clear signing)。

大多数重大的以太坊漏洞利用都有一个相同的最后步骤:用户批准了他们无法真正理解的交易。硬件钱包显示原始十六进制 calldata,更糟糕的是强迫你开启盲签。软件钱包会解码字段,但仅限于它们能识别合约时。当不能识别时,无论是由于协议是新的、应用被入侵,还是设备离线,用户都在盲目签名。

ERC-7730 定义了一种标准 JSON 格式,用于描述你的合约函数调用的含义

支持 ERC-7730 的钱包会读取你的描述符并显示:

交换

发送:1,000 USDC

至少接收:0.42 WETH

协议:Uniswap V3

或者一个人类和代理都能读懂的单一构造句:

用 1,000 USDC 交换至少 0.42 WETH

而不是一个函数选择器和一列原始整数值。

这就是清晰签名clear signing)——"所见即所签"。本教程将引导你为你的合约编写描述符,使用官方 CLI 工具进行验证,并提交到开放注册中心。

先决条件

  • 熟悉 Solidity 和智能合约 ABI
  • 一个已部署且 ABI 已验证的智能合约(描述符在被注册中心接受之前,需要通过 Sourcify 验证)
  • Python 3.12+(用于验证 CLI)
  • 基本 JSON 知识

什么是 ERC-7730 描述符?

描述符是一个包含三个部分的单一 JSON 文件:

部分 目的
context 通过链 ID 和地址将描述符绑定到特定的合约部署
metadata 命名项目并定义可复用的常量
display 将每个函数签名映射到人类可读的标签和字段格式

由于描述符与合约本身分离,你可以为任何现有协议添加清晰签名支持,而无需重新部署。钱包从注册中心获取描述符,并在签名时使用。

步骤 1:创建文件骨架

创建一个名为 calldata-<合约名>-<描述符版本>.json 的文件。calldata- 前缀告诉注册中心此描述符涵盖了合约函数调用,而 eip712- 用于类型化数据消息。描述符版本 告诉注册中心描述符文件的版本,如果未提供版本则默认为 0。

1{
2  "$schema": "https://eips.ethereum.org/assets/eip-7730/erc7730-v2.schema.json",
3  "context": {},
4  "metadata": {},
5  "display": {
6    "formats": {}
7  }
8}

步骤 2:编写 context 部分

context 部分将描述符绑定到一个或多个合约部署。钱包使用它来将传入交易与正确的描述符匹配。

1"context": {
2  "$id": "uniswap-v3-router-mainnet",
3  "contract": {
4    "deployments": [\
5      { "chainId": 1, "address": "0xYourContractAddressOnMainnet" },\
6      { "chainId": 137, "address": "0xYourContractAddressOnPolygon" }\
7    ]
8  }
9}

Context 字段

  • context.$id — 此描述符文档或部署配置的唯一标识符。
  • contract.deployments — 此描述符适用的部署集合。
  • deployments[].chainId — 部署的 EVM 链 ID,包含你合约部署的每条链。
  • deployments[].address — 钱包应与此描述符关联的合约地址,使用持有执行逻辑的实现地址。

步骤 3:编写 metadata 部分

metadata 部分提供关于此文件所描述的项目和合约的人类可读信息。钱包可能使用此信息在签名期间显示协议名称、链接和其他上下文细节。

1"metadata": {
2  "owner": "Example Swap Protocol",
3  "info": { "url": "https://example.xyz" },
4  "contractName": "SwapRouter"
5}

Metadata 字段

  • owner — 负责此描述符的项目、协议、组织或维护者。
  • info.url — 钱包可能向用户显示以获取更多上下文的规范项目或文档 URL。
  • contractName — 此文件描述的合约或实现名称,通常与已验证的源代码或 ABI 匹配。

如果你的 ERC-7730 文件描述的是 ERC-20 合约,还应添加一个 token 对象。

步骤 4:编写 display formats 部分

display.formats 对象将函数签名映射到人类可读的签名指令。这就是钱包在用户批准交易之前向用户展示你的函数的方式!

每个键都是一个人类可读的 ABI 片段——即函数签名,包含参数名称和参数类型,与你的 ABI 中的完全一致。

示例:描述代币兑换

"display": {
  "formats": {
    "swapExactTokensForTokens(uint256 amountIn,uint256 amountOutMin,address[] path,address to,uint256 deadline)": {
      "intent": "Swap",
      "interpolatedIntent": "Swap {amountIn} for at least {amountOutMin}",
      "fields": [\
        {\
          "path": "#.amountIn",\
          "label": "Send",\
          "format": "tokenAmount",\
          "params": {\
            "tokenPath": "#.path[0]"\
          }\
        },\
        {\
          "path": "#.amountOutMin",\
          "label": "Receive minimum",\
          "format": "tokenAmount",\
          "params": {\
            "tokenPath": "#.path[1]"\
          }\
        },\
        {\
          "path": "#.to",\
          "label": "Recipient",\
          "format": "addressName",\
          "params": {\
            "types": ["eoa", "contract"],\
            "sources": ["local", "ens"]\
          }\
        },\
        {\
          "path": "#.deadline",\
          "label": "Expires",\
          "format": "date",\
          "params": {\
            "encoding": "timestamp"\
          }\
        }\
      ]
    }
  }
}

Display 字段

  • intent(必填) 操作的简短用户友好描述,例如 "Swap"。
  • interpolatedIntent(推荐) 一个更丰富的句子模板,嵌入格式化的字段值,例如 "Swap {amountIn} for at least {amountOutMin}"。与 intent 一起包含,以提供更友好的描述符,钱包在满足任何显示约束的前提下可以选择显示。
  • fields(必填) 钱包应向用户显示的交易字段的有序列表。
    • path(必填) 对交易数据的引用。#.fieldName 指向按 ABI 中的名称解码后的 calldata 参数。@.value 指随交易发送的 ETH 值。
    • label(必填) 显示在值旁边的人类可读标签。
    • format(推荐) 控制值应如何渲染。常见格式包括:
      • tokenAmount
      • addressName
      • date
    • 当不需要额外格式化时使用 raw。某些格式接受额外的 params 配置。例如:
      • tokenAmount 可以使用 tokenPath 来标识哪个代币地址提供小数点和代码元数据。
      • date 可以使用 encoding 来描述时间戳的编码方式。
    • 如果所选格式不需要额外信息,省略 params

完整的描述符

{
  "$schema": "https://eips.ethereum.org/assets/eip-7730/erc7730-v2.schema.json",
  "context": {
    "$id": "uniswap-v3-router-mainnet",
    "contract": {
      "deployments": [\
        {\
          "chainId": 1,\
          "address": "0xYourContractAddressOnMainnet"\
        },\
        {\
          "chainId": 137,\
          "address": "0xYourContractAddressOnPolygon"\
        }\
      ]
    }
  },
  "metadata": {
    "owner": "Example Swap Protocol",
    "info": {
      "url": "https://example.xyz"
    },
    "contractName": "SwapRouter"
  },
  "display": {
    "formats": {
      "swapExactTokensForTokens(uint256 amountIn,uint256 amountOutMin,address[] path,address to,uint256 deadline)": {
        "intent": "Swap",
        "interpolatedIntent": "Swap {amountIn} for at least {amountOutMin}",
        "fields": [\
          {\
            "path": "#.amountIn",\
            "label": "Send",\
            "format": "tokenAmount",\
            "params": {\
              "tokenPath": "#.path[0]"\
            }\
          },\
          {\
            "path": "#.amountOutMin",\
            "label": "Receive minimum",\
            "format": "tokenAmount",\
            "params": {\
              "tokenPath": "#.path[1]"\
            }\
          },\
          {\
            "path": "#.to",\
            "label": "Recipient",\
            "format": "addressName",\
            "params": {\
              "types": ["eoa", "contract"],\
              "sources": ["local", "ens"]\
            }\
          },\
          {\
            "path": "#.deadline",\
            "label": "Expires",\
            "format": "date",\
            "params": {\
              "encoding": "timestamp"\
            }\
          }\
        ]
      }
    }
  }
}

步骤 5:提交到注册中心

ERC-7730 注册中心 是一个由 以太坊基金会 作为中立托管方托管的开放仓库。任何人都可以自由克隆并自行托管——钱包独立决定它们信任哪些注册中心实例。

  1. 在 GitHub 上 Fork 该仓库
  2. registry/<你的项目名称>/ 下创建一个文件夹
  3. 将你的文件放入其中:registry/myproject/calldata-mycontract-0_0.json
  4. $schema 字段更新为仓库中使用的相对路径:"../../specs/erc7730-v2.schema.json"
  5. 打开一个 Pull Request

当你打开 PR 时,CI 会自动运行模式验证,检查函数签名是否生成有效的选择器,确认合约地址已在 Sourcify 上验证,并标记 ABI 不一致。检查结果会内联显示在 PR 上。注册中心维护者会筛选提交,防止格式错误或潜在恶意的描述符。被纳入注册中心并不意味着经过审计或认可。

注意: 你的合约必须先通过 Sourcify 验证,PR 才能被接受。如果尚未验证,请先提交验证

合并后会发生什么?

注册中心中的所有描述符都对审计者开放。你的 PR 合并后,任何审计者都可以审查你的描述符,并发布加密证明(基于 ERC-8176)以确认其准确性。

这些证明信号让钱包可以应用自己的信任策略——一个有多个独立证明的描述符比没有证明的更具说服力。你可以通过 clearsigning.org 联系审计者社区。

钱包可以选择支持哪个注册中心。一旦你的描述符进入注册中心,支持 ERC-7730 的钱包如果信任该注册中心,就会开始获取它,并在用户与你的合约交互时显示人类可读的数据。

延伸阅读

  • 原文链接: ethereum.org/developers/...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~

相关文章

0 条评论