Перейти к содержанию

ERC-20 permit: разрешение по подписи владельца

Обычный approve требует транзакцию владельца. Расширение ERC-2612 позволяет подготовить подписанное разрешение и передать его другим участником. Это меняет способ установки allowance, но не превращает любую подпись в перевод или вход на сайт.

Создадим самостоятельный PermitLibraryCredit. Исходный LibraryCredit не получает permit задним числом: для другого интерфейса нужен новый отдельный экземпляр. Используем OpenZeppelin Contracts 5.4.0, где ERC20Permit реализует выбранную форму EIP-712-разрешения.

Полный токен с расширением

// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {ERC20Permit} from
    "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";

contract PermitLibraryCredit is ERC20, ERC20Permit {
    constructor(address initialHolder)
        ERC20("Permit Library Credit", "PLCR")
        ERC20Permit("Permit Library Credit")
    {
        _mint(initialHolder, 1000 * 10 ** 6);
    }
    function decimals() public pure override returns (uint8) {
        return 6;
    }
}

Название домена совпадает с именем токена. Версия EIP-712-домена в закреплённом расширении задаётся его конструктором как 1. Балансы и масштаб сохраняют знакомую лабораторную форму. Дополнительного mint или серверной авторизации здесь нет.

Импорт расширения даёт permit, nonces и DOMAIN_SEPARATOR по предусмотренному интерфейсу. Это не означает поддержку всех встречающихся методов с названием permit: существуют другие форматы и семантика. Клиент использует ABI конкретного известного токена.

Поля подписанного намерения

Подписываются owner, spender, value, nonce и deadline в выбранном домене. Домен связывает сообщение с сетью и проверяющим контрактом по правилам реализации. Поэтому подпись PLCR нельзя просто подать другому токену LCR по совпадению количества.

Домен: имя + версия + chainId + адрес PermitLibraryCredit
Намерение: owner=R, spender=C, value=3_000_000
Однократность: nonce из nonces(R)
Граница подачи: deadline в секундах

Это схема значений, не готовая подпись и не полученный результат. В программе выбираются реальные реквизиты локального экземпляра. Секретный ключ не записывается в страницу урока: кошелёк подписывает намерение своим предусмотренным механизмом.

Податель permit может отличаться от owner. Он доставляет уже разрешённые параметры, а не получает право изменить value или spender. Если сообщение изменено, подпись не подтверждает новый набор. По этой причине экран подписи должен показывать сумму и субъект расходования, а не называться общим «подключением».

Nonce и повтор

Перед подписью читается текущий nonces(R). Успешный permit использует это значение, увеличивает nonce и устанавливает allowance. Повтор того же разрешения не должен снова пройти как актуальное сообщение для прежнего nonce.

Порядок имеет значение. Если подготовлены две подписи с одним nonce, успешная подача одной сделает другую устаревшей по проверке. Параллельные вкладки не создают две независимые очереди nonce автоматически. Клиент должен учитывать состояние токена, а не только локальный счётчик формы.

При недопустимой подписи верхнее действие откатывается, включая расход nonce в этой области исполнения. Нельзя считать неуспешную попытку доказательством, что nonce уже окончательно изменился. Для восстановления нужно прочитать фактическое состояние.

Deadline ограничивает разрешение на подачу

В закреплённом ERC20Permit подпись отклоняется, если block.timestamp больше deadline. При равенстве отдельное условие срока ещё допускает её. Это отличается от строгой границы DefaultAdminRules ранее. Точный оператор сравнения относится к конкретному исходнику.

После успешной подачи allowance уже записан в токене. Истечение deadline не обнуляет его автоматически. Deadline ограничивает момент применения подписи, а не срок будущего расходования установленного лимита. Эта разница особенно важна для интерфейса, обещающего «разрешение только на пять минут».

Если проекту нужен срок расходования, требуется дополнительный механизм spender либо другая явно определённая реализация. Нельзя получить его только короткой датой в permit. Также подпись может быть удержана подателем до допустимого срока, поэтому близкий deadline уменьшает окно, но не заставляет другого участника обязательно доставить сообщение.

Permit не выполняет покупку

Успешный permit назначает allowance. Баланс owner не уменьшается от одной этой операции. Для получения токенов C по-прежнему выполняет transferFrom либо своё действие, которое вызывает его. Согласие на лимит и фактическая покупка остаются отдельными состояниями.

Комбинированный метод приложения может подать permit и затем выполнить действие, но такое соединение требует собственного разбора отказов и повторов. Например, кто-то может подать тот же permit раньше, а приложение должно осмысленно обработать актуальное allowance. Для первого токена мы не добавляем такой сложный маршрут.

Слово «газ не нужен владельцу» тоже требует точности. Кто-то всё равно отправляет транзакцию и оплачивает её по правилам сети. Податель может быть отдельным сервисом с собственной политикой. Подписанное сообщение не делает исполнение сети бесплатным.

Подписи контрактных кошельков

Закреплённый ERC20Permit использует восстановление ECDSA по указанной схеме. Это не автоматический вызов ERC-1271 любого смарт-кошелька. Для поддержки контрактных подписей нужен отдельный предусмотренный маршрут, который не следует приписывать этому расширению.

Поэтому клиент сначала определяет возможности выбранного токена и аккаунта. Он не предлагает одинаковую подпись всем кошелькам только потому, что они умеют показывать адрес. Для обычного approve контрактный аккаунт может выполнить собственный вызов, если его управление поддерживает это действие.

Отчёт о намерении

Сохраните owner, spender, базовое количество, nonce, deadline, домен и результат подачи. Сама подпись является разрешением, поэтому её нельзя считать безобидным диагностическим текстом для публичного журнала. Для учебного отчёта достаточно описания и известных результатов без секретов.

Для упражнения сравните два одинаковых сообщения с разными verifyingContract. Их домены разные. Затем сравните прежний nonce с новым после успеха. Так становится понятно, как подпись связана с назначением и однократностью. ERC-2612 и ERC20Permit 5.4.0 описывают точный договор.

Обновление разрешения после подписи

Пусть R подписал value=100 для S, а затем обычным approve установил 0. Подпись с ещё действующим nonce может снова поставить разрешение 100, если permit будет принят до deadline. Обычный approve в этой реализации не увеличивает nonce механизма permit. Поэтому отмена allowance и отмена неиспользованного подписанного сообщения не являются одним действием.

Это не повод придумывать общий метод отмены, которого у данного токена нет. Нужно описать фактические доступные маршруты: использование соответствующего nonce подходящей новой подписанной операцией либо ожидание истечения срока данного сообщения, с учётом порядка исполнения. На экране подписи показывайте полный spender и срок, чтобы пользователь понимал, какое сохраняющееся намерение выдаёт внешнему исполнителю.

Для самостоятельного уточнения: eip-2612, ERC20Permit.sol (5.4.0).

Оглавление курса. Листинги, расчёты и наблюдения здесь разобраны как ожидаемое поведение; компиляция, учебные программы и проверки при написании не запускались.

Оглавление серии