> ## Documentation Index
> Fetch the complete documentation index at: https://docs.monad.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# 如何让您的 Monad 合约支持清晰签名(ERC-7730)

export const CopyToClipboard = ({value, children}) => {
  const [copied, setCopied] = useState(false);
  const handleCopy = async () => {
    try {
      await navigator.clipboard.writeText(value);
      setCopied(true);
      setTimeout(() => setCopied(false), 1000);
    } catch {
      const textarea = document.createElement("textarea");
      textarea.value = value;
      textarea.style.position = "fixed";
      textarea.style.opacity = "0";
      document.body.appendChild(textarea);
      textarea.select();
      document.execCommand("copy");
      document.body.removeChild(textarea);
      setCopied(true);
      setTimeout(() => setCopied(false), 1000);
    }
  };
  return <span style={{
    display: "inline",
    whiteSpace: "nowrap"
  }}>
      {children}
      <button onClick={handleCopy} title={copied ? "Copied!" : "Copy to clipboard"} style={{
    background: "none",
    border: "none",
    cursor: "pointer",
    padding: "2px",
    display: "inline-flex",
    alignItems: "center",
    verticalAlign: "middle",
    marginLeft: "4px",
    opacity: copied ? 1 : 0.4,
    transition: "opacity 0.15s"
  }} onMouseEnter={e => {
    if (!copied) e.currentTarget.style.opacity = "0.8";
  }} onMouseLeave={e => {
    if (!copied) e.currentTarget.style.opacity = "0.4";
  }}>
        {copied ? <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#22c55e" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
            <polyline points="20 6 9 17 4 12" />
          </svg> : <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            <rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
            <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
          </svg>}
      </button>
    </span>;
};

ERC-7730 描述符告诉钱包如何用直白的语言展示您的合约调用和结构化数据签名。对于 Monad,过程与任何 EVM 链相同:编写描述符,将其绑定到 `chainId` 143 和您的合约地址,然后将其添加到公共注册表中。

## 什么是清晰签名?

[ERC-7730](https://eips.ethereum.org/EIPS/eip-7730) 是一种 JSON 格式,用于描述钱包应如何呈现合约 calldata 和 EIP-712 消息。描述符存在于您的合约之外。它将函数、消息类型和字段映射到钱包在用户签名之前可以显示的标签和格式化程序。

查询键是 `chainId` 加合约地址,因此 Monad 合约无需该标准的 Monad 特定版本。

如果没有描述符,钱包可能只有原始 calldata:

```text theme={null}
0x095ea7b3000000000000000000000000fe9c9ca3eed0fb3e6a5c0bf42ad6f1a0d1c7b2a40000000000000000000000000000000000000000000000000000000003b9aca00
```

有了描述符,相同的授权可以显示为用户可以检查的字段:

```text theme={null}
Approve USDC
Spender:  Uniswap V3 Router
Amount:   1,000 USDC
Network:  Monad
```

## 为什么这对 Monad 很重要

用户不应必须信任组装交易的网站。伪造的前端可以请求一个动作但实际提交另一个,而原始 calldata 对大多数用户来说没有任何有用的可检查信息。清晰签名将重要细节移至钱包提示中:动作、接收者、金额、代币、截止时间或对该调用重要的任何其他字段。

添加描述符不需要更改合约。您发布一个 JSON 文件,验证它,然后将其提交到支持 ERC-7730 的钱包所使用的注册表。

> Monad 主网 `chainId` 为 `143`(测试网为 `10143`)。请参阅[网络信息](/zh/developer-essentials/network-information)。

## 快速开始

先安装 [`uv`](https://docs.astral.sh/uv/)。`uvx` 命令可在不单独全局安装的情况下运行 `erc7730` 工具。

1. 从您的 ABI 生成初始描述符:

```bash theme={null}
uvx erc7730 generate \
  --chain-id 143 \
  --address 0xYourContractOnMonad \
  --abi ./abi/YourContract.json \
  --owner "Your Protocol" \
  --url "https://yourprotocol.xyz" \
  > calldata-yourcontract.json
```

2. 编辑意图和字段标签,使其读起来像英语(请参阅[编辑描述符](#editing-the-descriptor))。

3. 验证描述符:

```bash theme={null}
uvx erc7730 lint calldata-yourcontract.json
```

4. 在 [clear-signing.sourcify.dev](https://clear-signing.sourcify.dev) 上预览钱包如何渲染您的字段。

5. 向 [ERC-7730 注册表](https://github.com/ethereum/clear-signing-erc7730-registry)提交 PR。CI 会对其进行架构验证,由维护者审查,合并后,支持它的钱包即可使用。

请使用与协议或合约所有者关联的账户提交 PR。注册表维护者可能会要求提供您控制部署的证明。

## 保持 ABI 内联

`generate` 命令将您的合约 ABI 嵌入 `context.contract.abi` 下,使描述符自包含。请保持其存在:文件中包含 ABI,字段可以在本地验证,无需依赖任何区块浏览器即可理解该调用。

在 lint 时您可能会看到如下警告:

```text theme={null}
warning: Could not fetch ABI: Fetching reference ABI for chain id 143 failed, display fields will not be validated against ABI: ... Missing/Invalid API Key
```

此警告无害。除了内联 ABI 之外,`erc7730` 还会尝试从 Etherscan 获取已部署合约的 ABI 来交叉检查您的显示字段,该请求需要 `ETHERSCAN_API_KEY`。设置一个(`export ETHERSCAN_API_KEY=...`),lint 就能顺利通过;不设置的话,该交叉检查会被跳过。无论哪种方式,都请保留内联的 `abi`。

```json theme={null}
{
  "context": {
    "contract": {
      "deployments": [
        { "chainId": 143, "address": "0xYourContractOnMonad" }
      ],
      "abi": [
        {
          "type": "function",
          "name": "approve",
          "inputs": [
            { "name": "spender", "type": "address" },
            { "name": "amount", "type": "uint256" }
          ]
        }
      ]
    }
  }
}
```

## 编辑描述符

`generate` 会为您生成一个骨架,每个函数对应一条条目。大部分手动编辑集中在两个地方:

* **`intent`**:一个简短的动作标签,例如 `Approve USDC` 或 `Wrap MON`。请保持在 30 个字符以内;某些硬件钱包屏幕会截断较长的字符串。
* **`fields`**:您希望用户审查的参数,每个参数都有 `label` 和 `format`。

请使用最贴切的 formatter 来匹配该值:

| `format`      | 渲染                | 备注                                                         |
| ------------- | ----------------- | ---------------------------------------------------------- |
| `tokenAmount` | `1,000 USDC`      | 应用小数位和代币代码。为最大授权设置 `threshold` 和 `message`(如 `Unlimited`)。 |
| `amount`      | 带代码的原生 MON        | 读取交易的 `value`。                                             |
| `addressName` | 已知名称,否则为带校验的地址    | 使用 `types`(eoa、contract、token)和 `sources`(local、ens)。      |
| `raw`         | 数字、字符串或原样的地址      |                                                            |
| `date`        | 从 Unix 时间戳解析的可读日期 | 设置 `encoding`。                                             |

字段路径使用三个根:`#.` 表示已解码的 calldata 或消息字段,`$.` 表示此描述符自身的元数据,`@.` 表示交易容器,例如 `@.value`。

## Monad 示例

以下 Monad 描述符在您编写自己的描述符时是有用的参考:

<ul>
  <li>
    <strong>Permit2</strong>,许多 dApp 使用的 EIP-712 授权合约,地址为 <CopyToClipboard value="0x000000000022D473030F116dDEE9F6B43aC78BA3">[`0x000000000022D473030F116dDEE9F6B43aC78BA3`](https://monadvision.com/address/0x000000000022D473030F116dDEE9F6B43aC78BA3)</CopyToClipboard>,已在 [注册表 PR #2611](https://github.com/ethereum/clear-signing-erc7730-registry/pull/2611) 中添加到 Uniswap 的规范描述符。
  </li>

  <li>
    <strong>Monad 质押预编译合约</strong>(delegate、undelegate、claim rewards),地址为 <CopyToClipboard value="0x0000000000000000000000000000000000001000">[`0x0000000000000000000000000000000000001000`](https://monadvision.com/address/0x0000000000000000000000000000000000001000)</CopyToClipboard>,详见 [注册表 PR #2589](https://github.com/ethereum/clear-signing-erc7730-registry/pull/2589)。
  </li>

  <li>
    <strong>Wrapped MON</strong>(wrap、unwrap 和 ERC-20 调用),地址为 <CopyToClipboard value="0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A">[`0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A`](https://monadvision.com/address/0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A)</CopyToClipboard>。
  </li>
</ul>

## 钱包如何使用描述符

从高层次上讲,支持它的钱包会执行以下操作:

1. 用户发起一笔交易或对 EIP-712 消息进行签名。
2. 钱包计算 4 字节的 selector(或 EIP-712 类型哈希)。
3. 它通过 `chainId` 和地址查找描述符。
4. 它检查描述符的 `context` 绑定与实际目标是否匹配。
5. 它使用 ABI 解码参数,并解析代币和名称的元数据。
6. 它应用每个字段 formatter,并显示 `intent` 及格式化后的字段。

如果找不到描述符,钱包将回退到默认签名视图。

## 资源

* [ERC-7730 规范](https://eips.ethereum.org/EIPS/eip-7730)
* [ERC-7730 注册表](https://github.com/ethereum/clear-signing-erc7730-registry)
* [Sourcify 预览工具](https://clear-signing.sourcify.dev)
* [clearsigning.org](https://clearsigning.org)
* [网络信息(chainId 143)](/zh/developer-essentials/network-information)
* [Monad 开发者 Discord](https://discord.gg/monaddev)
