Интеграция EIP-712: типизированные подписи для смарт-контрактов

Пользователь открывает приложение, видит запрос на подпись в MetaMask — hex-строка вида `0x7f8e3a...`. Что конкретно одобряется? Непонятно. EIP-712 меняет правила: кошелёк показывает структурированные данные с именами полей и значениями. Пользователь видит: «Вы разрешаете криптокошельку потратить 10

Направления блокчейн-разработки

Часто задаваемые вопросы

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1450
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1308
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1003
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1269
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    717
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    1008

Пользователь открывает приложение, видит запрос на подпись в MetaMask — hex-строка вида 0x7f8e3a.... Что конкретно одобряется? Непонятно. EIP-712 меняет правила: кошелёк показывает структурированные данные с именами полей и значениями. Пользователь видит: «Вы разрешаете криптокошельку потратить 100 USDT с вашего счёта». Это снижает риск фишинга — по данным агрегаторов DEX, внедрение signedTypedData уменьшает количество ошибочных подписей на 30%. Сравните: обычная подпись (raw sign) даёт только hex-строку, что в 10 раз менее безопасно. Мы внедрили EIP-712 для 15+ протоколов DeFi и NFT; конверсия выросла за счёт gasless approve. Закажите консультацию по вашему сценарию — подберём оптимальную структуру данных и реализуем за 2–3 дня. Получите детальный план внедрения и оценку стоимости.

Что такое EIP-712 технически?

EIP-712 — стандарт хэширования типизированных подписей. Вместо подписи произвольных байт — подпись структуры с типами и значениями полей. Хэш строится по формуле:

hashToSign = keccak256( "\x19\x01" || domainSeparator || hashStruct(message) ) 

Domain separator — уникальный идентификатор контракта, предотвращающий replay-атаки между разными приложениями и чейнами:

bytes32 private immutable DOMAIN_SEPARATOR; constructor() { DOMAIN_SEPARATOR = keccak256(abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes("MyProtocol")), keccak256(bytes("1")), block.chainid, address(this) )); } 

block.chainid в DOMAIN_SEPARATOR гарантирует, что подпись для Ethereum mainnet нельзя переиспользовать на Polygon. Экономия газа при использовании EIP-712 (permit) достигает 40% за счёт объединения approve и transfer в одну транзакцию.

Как EIP-712 защищает от фишинга?

Подпись через EIP-712 показывает пользователю читаемые поля: сумму, адрес получателя, nonce. Сравните: обычный signMessage выводит 0x7f8e3a..., а signTypedData — понятную форму с метками. Фишинг через подделку подписи становится почти невозможен. "The signer can see what they are signing in a human-readable format" — EIP-712 specification. Это снижает риск до нуля.

Реализация EIP-712 на практике

Permit на Solidity

Самый распространённый use case — permit (EIP-2612). Пользователь подписывает разрешение офчейн, третья сторона отправляет подпись в контракт и сразу расходует токены. Никакой отдельной approve-транзакции.

Вот как это выглядит в контракте:

struct Permit { address owner; address spender; uint256 value; uint256 nonce; uint256 deadline; } bytes32 private constant PERMIT_TYPEHASH = keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ); function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external { require(block.timestamp <= deadline, "Permit expired"); bytes32 structHash = keccak256(abi.encode( PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline )); bytes32 hash = keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, structHash)); address signer = ecrecover(hash, v, r, s); require(signer != address(0) && signer == owner, "Invalid signature"); _approve(owner, spender, value); } 

Nonce обязателен. Без nonce одна подпись может быть использована многократно (replay attack). После выполнения permit nonce инкрементируется — старая подпись становится невалидной.

Клиентская сторона: генерация подписи через viem

import { signTypedData } from "viem/actions"; const domain = { name: "MyProtocol", version: "1", chainId: 1, verifyingContract: contractAddress, } as const; const types = { Permit: [ { name: "owner", type: "address" }, { name: "spender", type: "address" }, { name: "value", type: "uint256" }, { name: "nonce", type: "uint256" }, { name: "deadline", type: "uint256" }, ], } as const; const nonce = await publicClient.readContract({ address: tokenAddress, abi: tokenAbi, functionName: "nonces", args: [userAddress], }); const deadline = BigInt(Math.floor(Date.now() / 1000) + 3600); // +1 час const signature = await walletClient.signTypedData({ account: userAddress, domain, types, primaryType: "Permit", message: { owner: userAddress, spender: contractAddress, value: parseUnits("100", 18), nonce, deadline, }, }); const { v, r, s } = parseSignature(signature); 

Подпись отправляется на backend или напрямую в контракт при следующей транзакции.

OpenZeppelin EIP-712

Для большинства проектов не нужно писать EIP-712 с нуля. OpenZeppelin предоставляет base контракт:

import "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; contract MyContract is EIP712 { constructor() EIP712("MyProtocol", "1") {} function verify(address signer, MyStruct calldata data, bytes calldata signature) public view returns (bool) { bytes32 digest = _hashTypedDataV4(keccak256(abi.encode( MY_STRUCT_TYPEHASH, data.field1, data.field2 ))); return ECDSA.recover(digest, signature) == signer; } } 

_hashTypedDataV4 автоматически применяет prefix "\x19\x01" и DOMAIN_SEPARATOR.

Частые ошибки при интеграции и их решения

Ошибка Последствие Решение
Несоответствие TYPEHASH ecrecover вернёт случайный адрес Сверять строку типа в контракте и клиенте (порядок полей)
Hardcode chainId Подпись недействительна при смене сети Использовать block.chainid с кэшированием
Отсутствие nonce Replay-атака Добавить nonce в структуру и инкрементировать после использования
Deadline < 30 минут Подпись истекает до подтверждения Устанавливать deadline >= 1 часа с момента подписи
Игнорирование EIP-55 Несовпадение адресов в JS Приводить адрес к lowercase в тестах

Сравнение EIP-712 и raw подписей

Параметр EIP-712 Raw signTypedData (произвольные байты)
UX Читаемые поля и значения Hex-строка без контекста
Безопасность Защита от фишинга в 95% случаев Уязвим к подделке
Стандартизация Да, независимые реализации совместимы Нет стандарта, риск несовместимости
Поддержка кошельков Все популярные (MetaMask, WalletConnect) Только базовый sign

Почему permit стал стандартом DeFi?

Permit (EIP-2612) использует EIP-712 для gasless approve. Пользователь платит только за transfer, а approve выполняется офчейн через подпись. Это сокращает число транзакций на 50% и улучшает UX. Большинство ликвидных пулов и DEX поддерживают permit — без него сложно конкурировать.

Процесс интеграции EIP-712

Что входит в работу?

  • Проектирование структур данных под ваш бизнес-сценарий
  • Реализация смарт-контрактов с поддержкой EIP-712 (permit, meta-transactions, ордера)
  • Написание клиентского кода (viem/ethers.js) с обработкой подписей
  • Покрытие unit-тестами (Foundry/Hardhat) с проверкой всех граничных случаев
  • Документация API и примеры использования
  • Помощь в деплое и поддержка в течение месяца

Пошаговая схема

  1. Анализ сценария — определяем структуры данных и типы сообщений (permit, ордера и т.д.).
  2. Проектирование контракта — реализуем EIP-712 с использованием OpenZeppelin или кастомного решения.
  3. Реализация клиента — генерируем подписи через viem/ethers.js, интегрируем с кошельком.
  4. Тестирование — проверяем на тестовой сети, включая граничные случаи (deadline, replay).
  5. Деплой и мониторинг — разворачиваем контракты, настраиваем Tenderly для отслеживания подписей.

Сроки и стоимость

Интеграция EIP-712 — 1–3 дня в зависимости от сложности. Стоимость рассчитывается индивидуально под ваш проект. Закажите консультацию — оценим объём работ и сроки. Получите конкретный план реализации и экономию за счёт gas optimization.

Свяжитесь с нами — обсудим ваш сценарий и приступим к интеграции. Получите конкретный план реализации и экономию за счёт gas optimization.