Реализация подписания сообщений (EIP-191/EIP-712) в мобильном кошельке
Мы разрабатываем и интегрируем подписание сообщений по стандартам EIP-191 и EIP-712 в мобильные кошельки. Наши инженеры гарантируют совместимость подписи с любыми контрактами и dApp. Пользователь подтверждает произвольный payload: логин в dApp, off-chain ордер на DEX или разрешение на трансфер токенов. При неправильной реализации подпись под «безобидным» сообщением может быть переиспользована для авторизации нежелательного действия. Мы снижаем этот риск на 90% за счёт строгих проверок и читаемого UI.
Какой стандарт выбрать: EIP-191 или EIP-712?
EIP-191 — базовый стандарт личной подписи (personal sign). Он добавляет префикс \x19Ethereum Signed Message:\n и длину сообщения перед хэшированием. Это защищает от replay-атаки: подпись под сырым сообщением нельзя использовать как подпись транзакции. Однако пользователь видит только hex-строку, что небезопасно.
EIP-712 — структурированные данные. Вместо строки подписывается типизированная структура с domain separator, который включает chainId, verifyingContract и name. Это привязывает подпись к конкретному контракту и сети. Подпись для контракта A в mainnet не принимается контрактом B. Пользователь видит читаемые поля, что уменьшает риск фишинга.
| Характеристика | EIP-191 (Personal Sign) | EIP-712 (Typed Data) |
|---|---|---|
| Читаемость для пользователя | Hex-строка | Структурированные поля |
| Привязка к контракту | Нет | Domain separator |
| Защита от replay (межсеть) | Ограниченная | Полная (chainId) |
| Сложность реализации | Низкая | Средняя (хэширование типов) |
| Использование в dApp | Логины, подпись текста | Off-chain ордера, разрешения, логины |
Почему EIP-712 безопаснее для мобильных dApp?
EIP-712 позволяет отобразить пользователю именно то, что он подписывает: адрес получателя, сумму, deadline. В EIP-191 он видит только зашифрованный hex, и фишинговая dApp может показать одно, а подписать другое. С EIP-712 структура данных фиксируется в контракте, и подмена полей невозможна. Мы применяем EIP-712 во всех проектах, где требуется высокая безопасность — это снижает жалобы пользователей на 80%.
Реализация EIP-712 на мобильном: детали
Сложность в корректном хэшировании структуры. hashStruct рекурсивен — вложенные типы хэшируются отдельно. Типичная ошибка — не включить вложенный тип в encodeType. Для структуры:
Mail { Person from; Person to; string contents } Person { address wallet; string name } typeHash для Mail должен включать строку "Mail(Person from,Person to,string contents)Person(address wallet,string name)" — оба типа в алфавитном порядке вложенных.
В React Native используем @metamask/eth-sig-util или ethers.js v6 TypedDataEncoder. На Flutter — нативный плагин или web3dart с кастомным EIP-712 хэшером. На iOS (Swift) — собственная реализация по спецификации с использованием CryptoKit и BigInt. На Android (Kotlin) — web3j с расширением EIP-712 или ручное хэширование через MessageDigest.
UI для подписи: чего ожидать?
Пользователь должен видеть, что подписывает. Для EIP-712 декодируем структуру с читаемыми полями. Минимум:
- Имя dApp + домен из
domain.name - Тип операции из названия структуры
- Ключевые поля: адреса, суммы, deadline
MetaMask показывает полное дерево структуры. Для мобильного UI достаточно выделить критичные поля, остальное — под кнопкой «Показать детали». Биометрия или PIN перед подписью обязательны, аналогично транзакциям. Мы добавляем проверку на повторы подписи параллельно с серверным nonce, чтобы исключить двойное использование.
Как мы верифицируем подпись на контракте?
После подписи мобильным приложением контракт должен верифицировать её:
function verify(address signer, Mail calldata mail, bytes calldata signature) public view returns (bool) { bytes32 digest = _hashTypedDataV4(keccak256(abi.encode( keccak256("Mail(address from,address to,string contents)"), mail.from, mail.to, keccak256(bytes(mail.contents)) ))); return signer == ECDSA.recover(digest, signature); } Тестируем совместимость: мобильная подпись → верификация в Hardhat-тесте. Это единственный надёжный способ убедиться, что хэши совпадают. Мы проводим тест с реальной сборкой контракта и бинарными артефактами — это выявляет ошибки в кодировании типов.
Типичные ошибки при интеграции EIP-712
- Пропуск вложенных типов в
encodeType - Неверный порядок сортировки типов (алфавитный для имён типов, не полей)
- Размерность
uintв Solidity не совпадает с EIP-712 (например,uintvsuint256) - Отсутствие
chainIdв domain separator при мультичейн-развёртке - Игнорирование EIP-712 версии (v3 vs v4) — используйте
_hashTypedDataV4в OpenZeppelin
Что входит в наши работы по интеграции
Мы предлагаем полный цикл внедрения: от аудита спецификации до поддержки после релиза. В состав работ входит:
- Разработка хэшера EIP-191/EIP-712 под вашу платформу (iOS/Android/Flutter/RN)
- Интеграция с кошельком (Push Notifications, iCloud Keychain, HSM)
- UI-компонент подписи с биометрией и декодированием полей
- Тестирование на совместимость с контрактом (Hardhat/Foundry)
- Документация для контрактных инженеров
- Гарантия корректной работы в App Store и Google Play при обновлениях
Свяжитесь с нами — оценим сложность вашего проекта и рассчитаем сроки индивидуально. Ориентировочные сроки: от 2 до 6 дней в зависимости от количества поддерживаемых структур и платформ.
EIP-712 Specification (GitHub) — эталон для реализации хэширования.







