---
title: "Добавьте понятное подписание в ваш протокол с помощью ERC-7730"
description: "Узнайте, как написать дескриптор ERC-7730, чтобы взаимодействия с вашим смарт-контрактом отображали понятные человеку детали в кошельках перед подписанием пользователями."
author: "Хестер Брюкман"
lang: ru
tags: ["ERC-7730", "безопасность", "подписание", "смарт-контракты", "кошельки"]
skill: intermediate
breadcrumb: "Понятное подписание"
published: 2026-05-11
---

Большинство крупных эксплойтов в Эфириуме имели один и тот же последний шаг: пользователь одобрял транзакцию, которую не мог осмысленно понять. Аппаратные кошельки показывают необработанные шестнадцатеричные данные вызова (calldata) и, что еще хуже, заставляют вас включать слепое подписание. Программные кошельки показывают декодированные поля, но только тогда, когда они распознают контракт. Когда они этого не делают, будь то из-за того, что протокол новый, приложение скомпрометировано или устройство не в сети, пользователи подписывают вслепую.

[ERC-7730](https://eips.ethereum.org/EIPS/eip-7730) определяет стандартный формат JSON для описания того, что *означают* вызовы функций вашего контракта. 

Кошелек, поддерживающий ERC-7730, читает ваш дескриптор и показывает:

> **Обменять**  
> Отправить: 1000 USDC  
> Получить минимум: 0.42 WETH  
> Протокол: Юнисвоп V3

Или одно составленное предложение, понятное как людям, так и агентам:

> Обменять 1000 USDC как минимум на 0.42 WETH

Вместо селектора функции и списка необработанных целочисленных значений.

Это [понятное подписание](https://clearsigning.org/) — «Что видишь, то и подписываешь» (What You See Is What You Sign). В этом руководстве рассказывается, как написать дескриптор для вашего собственного контракта, проверить его с помощью официального инструмента CLI и отправить в открытый реестр.

## Предварительные требования {#prerequisites}

- Знакомство с Solidity и ABI смарт-контрактов
- Развернутый смарт-контракт с верифицированным ABI (верификация в [Sourcify](https://sourcify.dev) обязательна перед принятием дескриптора в реестр) 
- Python 3.12+ для CLI-инструмента валидации 
- Базовые знания JSON

## Что такое дескриптор ERC-7730? {#what-is-an-erc-7730-descriptor}

Дескриптор — это один JSON-файл, состоящий из трех разделов:

| Раздел | Назначение |
| :---- | :---- |
| `context` | Привязывает дескриптор к конкретным развертываниям контракта по ID цепи и адресу |
| `metadata` | Задает имя проекта и определяет переиспользуемые константы |
| `display` | Сопоставляет каждую сигнатуру функции с понятными человеку метками и форматами полей |

Поскольку дескриптор отделен от самого контракта, вы можете добавить поддержку понятного подписания в любой существующий протокол без повторного развертывания. Кошельки извлекают дескрипторы из реестра и используют их во время подписания.

## Шаг 1: Создайте каркас файла {#step-1-create-the-file-skeleton}

Создайте файл с именем `calldata-<contractname>-<descriptorversion>.json`. Префикс `calldata-` сообщает реестру, что этот дескриптор охватывает вызовы функций контракта, в отличие от `eip712-` для сообщений с типизированными данными. Поле `descriptorversion` сообщает реестру версию файла дескриптора (по умолчанию 0, если версия не указана).


```json
{
  "$schema": "https://eips.ethereum.org/assets/eip-7730/erc7730-v2.schema.json",
  "context": {},
  "metadata": {},
  "display": {
    "formats": {}
  }
}
```

## Шаг 2: Напишите раздел контекста {#step-2-write-the-context-section}

Раздел `context` привязывает дескриптор к одному или нескольким развертываниям контракта. Кошельки используют это для сопоставления входящей транзакции с правильным дескриптором.

```json
"context": {
  "$id": "uniswap-v3-router-mainnet",
  "contract": {
    "deployments": [
      { "chainId": 1, "address": "0xYourContractAddressOnMainnet" },
      { "chainId": 137, "address": "0xYourContractAddressOnPolygon" }
    ]
  }
}
```

### Поля контекста {#context-fields}

- `context.$id` — Уникальный идентификатор для этого документа дескриптора или конфигурации развертывания.
- `contract.deployments` — Набор развертываний, к которым применяется этот дескриптор.
- `deployments[].chainId` — ID цепи EVM для развертывания. Включите каждую цепь, где развернут ваш контракт.
- `deployments[].address` — Адрес контракта, который кошельки должны ассоциировать с этим дескриптором. Используйте адрес реализации, содержащий логику выполнения.

## Шаг 3: Напишите раздел метаданных {#step-3-write-the-metadata-section}

Раздел метаданных предоставляет понятную человеку информацию о проекте и контракте, описываемых этим файлом. Кошельки могут использовать эту информацию для отображения названий протоколов, ссылок и других контекстных деталей во время подписания.

```json
"metadata": {
  "owner": "Example Swap Protocol",
  "info": { "url": "https://example.xyz" },
  "contractName": "SwapRouter"
}
```

### Поля метаданных {#metadata-fields}

- `owner` — Проект, протокол, организация или мейнтейнер, ответственные за этот дескриптор.
- `info.url` — Канонический URL-адрес проекта или документации, который кошельки могут отображать пользователям для дополнительного контекста.
- `contractName` — Имя контракта или реализации, описываемое этим файлом, обычно совпадающее с верифицированным исходным кодом или ABI.

Если ваш файл ERC-7730 описывает контракт ERC-20, вам также следует добавить объект токена. 

## Шаг 4: Напишите раздел форматов отображения {#step-4-write-the-displayformats-section}

Объект `display.formats` сопоставляет сигнатуры функций с понятными человеку инструкциями по подписанию. Именно так кошельки показывают вашу функцию пользователям перед тем, как они одобрят транзакцию!

Каждый ключ — это понятный человеку фрагмент ABI: сигнатура функции, включающая как имена параметров, так и их типы в точности так, как они указаны в вашем ABI.


### Пример: Описание свопа токенов {#eg-describing-token-swap}

```json
"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-fields}

- **`intent`** — **(Обязательно)** Краткое, удобное для пользователя описание действия, например «Обменять».
- **`interpolatedIntent`** — **(Рекомендуется)** Более развернутый шаблон предложения, в который встраиваются отформатированные значения полей, например `"Swap {amountIn} for at least {amountOutMin}"`. Включите его вместе с `intent`, чтобы предоставить еще более удобный для пользователя дескриптор, который кошельки могут выбрать для показа с учетом любых ограничений отображения.
- **`fields`** — **(Обязательно)** Упорядоченный список полей транзакции, которые кошельки должны отображать пользователям.
  - **`path`** — **(Обязательно)** Ссылка на данные транзакции. `#.fieldName` указывает на декодированный параметр данных вызова по имени в ABI. `@.value` относится к значению ETH, отправленному с транзакцией.
  - **`label`** — **(Обязательно)** Понятная человеку метка, отображаемая рядом со значением.
  - **`format`** — **(Рекомендуется)** Управляет тем, как должно отображаться значение. Общие форматы включают:
    - `tokenAmount`
    - `addressName`
    - `date`

    Используйте `raw`, когда дополнительное форматирование не требуется. Некоторые форматы принимают дополнительную конфигурацию **`params`**. Например:

    - `tokenAmount` может использовать `tokenPath` для определения того, какой адрес токена предоставляет метаданные о десятичных знаках и тикере.
    - `date` может использовать `encoding` для описания того, как закодирована временная метка.

    Если выбранный формат не требует дополнительной информации, опустите `params`.

## Полный дескриптор {#the-complete-descriptor}

```json
{
  "$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: Отправьте в реестр {#step-5-submit-to-the-registry}

[Реестр ERC-7730](https://github.com/ethereum/clear-signing-erc7730-registry) — это открытый репозиторий, размещенный [Фондом Ethereum](/foundation/) в качестве нейтрального управляющего. Любой желающий может клонировать и самостоятельно разместить его — кошельки независимо решают, каким экземплярам реестра они доверяют.

1. Сделайте форк репозитория на GitHub  
2. Создайте папку по пути `registry/<your-project-name>/`  
3. Поместите ваш файл внутрь нее: `registry/myproject/calldata-mycontract-0_0.json`  
4. Обновите поле `$schema` на относительный путь, используемый в репозитории: `"../../specs/erc7730-v2.schema.json"`  
5. Откройте pull request

Когда вы открываете PR, CI автоматически запускает валидацию схемы, проверяет, что сигнатуры функций создают действительные селекторы, подтверждает, что адрес контракта верифицирован в Sourcify, и отмечает несоответствия ABI. Результаты проверки появляются прямо в PR. Мейнтейнеры реестра проверяют отправленные данные на наличие неправильно сформированных или потенциально вредоносных дескрипторов. Включение в реестр не подразумевает аудита или одобрения.

<Alert variant="info">
<AlertContent>
<AlertDescription>
**Примечание:** Ваш контракт должен быть верифицирован в <a href="https://repo.sourcify.dev">Sourcify</a> до того, как ваш PR будет принят. Если он еще не верифицирован, сначала <a href="https://verify.sourcify.dev/">отправьте его на верификацию</a>.
</AlertDescription>
</AlertContent>
</Alert>

## Что происходит после слияния? {#what-happens-after-merging}

Все дескрипторы в реестре открыты для аудиторов. После слияния вашего PR любой аудитор может проверить ваш дескриптор и опубликовать криптографическую аттестацию (в рамках [ERC-8176](https://github.com/ethereum/ERCs/pull/1576)), подтверждающую его точность. 

Эти сигналы аттестации позволяют кошелькам применять свои собственные политики доверия — дескриптор с несколькими независимыми аттестациями имеет больший вес, чем дескриптор без них. Вы можете связаться с сообществом аудиторов через [clearsigning.org](https://clearsigning.org).

Кошельки сами выбирают, какой реестр они будут поддерживать. Как только ваш дескриптор окажется в реестре, кошельки, поддерживающие ERC-7730, начнут извлекать его, если он есть в их реестре, и будут отображать понятные человеку данные, когда пользователи взаимодействуют с вашим контрактом.

## Дополнительная литература {#further-reading}

- [Спецификация ERC-7730](https://eips.ethereum.org/EIPS/eip-7730)  
- [Реестр ERC-7730](https://github.com/ethereum/clear-signing-erc7730-registry)  
- [clearsigning.org](https://clearsigning.org) — инструменты, статус экосистемы и управление  
- [Верификация контрактов в Sourcify](https://sourcify.dev)  
- [Инициатива Trillion Dollar Security](https://trilliondollarsecurity.org)