Шаблоны контроля доступа в смарт-контрактах: owner, роли и стороны сделки
Как контракты решают, кому что можно: Ownable и Ownable2Step, роли, проверка сторон сделки, паузы и минимальные права. Разбор на примере эскроу.
В этой статье
Каждая функция контракта отвечает на вопрос: «кому это позволено?» Ошибка в ответе - самая частая причина потери денег: функция вывода без проверки, забытая роль, украденный ключ владельца. Здесь разберём основные шаблоны и посмотрим, как их применяет контракт эскроу. Для контекста про подписи и адреса - криптография с открытым ключом.
Откуда контракт знает, кто вызывает
Внутри функции доступен msg.sender - адрес, который непосредственно вызвал контракт. Он не подделывается: подпись транзакции проверена сетью до исполнения (как это работает). Весь контроль доступа строится на сравнении msg.sender с ожидаемым адресом или ролью.
Шаблон 1. Единственный владелец (Ownable)
Простейшая схема: адрес owner, и функции с модификатором onlyOwner.
function setTokenAllowed(address token, bool allowed) external onlyOwner {
allowedToken[token] = allowed;
}Проблемы схемы: потерянный ключ означает потерянный контроль, украденный - захват. Обезопасить помогают мультиподпись и таймлок.
Шаблон 2. Двухэтапная передача владения (Ownable2Step)
В простом Ownable передача владельца в одном вызове: опечатка в адресе - и контроль утрачен навсегда. Ownable2Step требует двух шагов:
- Текущий владелец вызывает
transferOwnership(newOwner). - Новый владелец вызывает
acceptOwnership().
Пока второй шаг не сделан, ничего не меняется. Опечатка не страшна, потому что по неверному адресу никто не «примет». Контракт эскроу использует именно эту схему.
Шаблон 3. Роли (AccessControl)
Если у контракта несколько административных обязанностей, давать все права одному адресу рискованно. AccessControl вводит роли - идентификаторы (bytes32), которые можно выдавать и отзывать:
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
function pause() external onlyRole(PAUSER_ROLE) { ... }Принцип минимальных прав: каждой роли и каждому адресу - только то, что ему необходимо. Оператору паузы не нужно право выводить комиссии.
Шаблон 4. Стороны сделки
В эскроу главная авторизация - не владелец, а участники конкретной сделки. Права определяются данными сделки:
| Действие | Кто может | Как проверяется |
|---|---|---|
| Принять оферту | контрагент | msg.sender == (оплачена ? продавец : покупатель) |
| Подтвердить получение | покупатель | msg.sender == d.buyer |
| Открыть спор | покупатель или продавец | msg.sender равен d.buyer или d.seller |
| Вынести решение | назначенный гарант | msg.sender == d.arbiter |
Ошибки возвращаются как NotAuthorized(). Подробнее о потоке - в жизненном цикле.
Шаблон 5. Проверка состояния как часть доступа
Авторизация - это не только «кто», но и «когда». Каждая функция сделки требует нужного статуса:
_requireStatus(d, Status.Active);Нельзя подтвердить получение по оферте, нельзя открыть спор по закрытой сделке. Это конечный автомат (разбор).
Шаблон 6. Функции «для всех» ради живучести
Иногда лучший контроль доступа - его отсутствие. Например, после истечения срока оферты cancelOffer может вызвать любой. Зачем? Чтобы деньги не зависели от активности сторон: если покупатель потерял ключ, а продавец исчез, кто-то посторонний (в том числе сервис) может вернуть средства. Возврат всё равно идёт только покупателю, поэтому злоупотребить функцией нельзя.
Это принцип проектирования: «кто угодно может запустить, но результат определён заранее».
Шаблон 7. Пауза
Pausable позволяет владельцу остановить часть функций при инциденте. В эскроу пауза блокирует только создание и принятие оферт:
function createOffer(...) external payable whenNotPaused nonReentrant { ... }Выход из активной сделки всегда работает. Владелец не может заморозить чьи-то деньги (почему это важно).
Что может и чего не может владелец эскроу
| Может | Не может |
|---|---|
| Разрешить или запретить токен для новых сделок | Забрать средства активных сделок |
| Отметить гаранта «проверенным» | Менять результат сделки |
| Поставить на паузу новые сделки | Остановить выплаты |
| Забрать накопленные комиссии | Забрать зарезервированное (reserved) |
| Забрать «ничьи» токены | Изменить код контракта |
Гарантия строится на инварианте: баланс контракта не меньше всего, что он должен (reserved). Административные функции могут тронуть лишь превышение. Подробнее - в разборе конечного автомата.
Типичные ошибки
- Забытый
onlyOwnerна административной функции. tx.originвместоmsg.sender.- Публичная функция инициализации, которую может вызвать кто угодно (прокси).
renounceOwnershipвызван случайно: контракт остаётся навсегда без управления.- Бэкдоры: скрытые роли в коде (аудиты).
- Слишком широкие права одной роли.
Что почитать дальше
Поговорим об атаках, которые обходят даже верный контроль доступа: реентерабельность и pull-платежи и каталог уязвимостей.
Читайте также
- 2 · Практика
Мультиподпись и таймлок: как управлять контрактом без единой точки отказа
Что такое мультиподпись (m из n) и таймлок, как работают кошельки вроде Safe, какие риски они закрывают, а какие нет, и как применять их для владельца и гаранта.
3 мин чтения - 3 · Под капотом
Пишем эскроу на Solidity: конечный автомат и разбор реального контракта
Как устроен эскроу-контракт в коде: статусы, переходы, проверки доступа, события и расчёт выплат. Минимальная версия с нуля и сравнение с боевым контрактом.
7 мин чтения
Упоминается в статьях
- Как проверить смарт-контракт перед использованием: пошаговая инструкция
- Мультиподпись и таймлок: как управлять контрактом без единой точки отказа
- Обзор языка Solidity: типы, функции, ошибки и события на примерах
- ERC-20 с нуля: пишем токен на Solidity и разбираем каждую строку
- Подписи в Ethereum: ECDSA, ecrecover, personal_sign и EIP-712
- Пишем эскроу на Solidity: конечный автомат и разбор реального контракта
- Раскладка хранилища и упаковка переменных в Solidity
- Каталог уязвимостей смарт-контрактов: 12 классических ошибок и защита от них
- Прокси и обновляемые контракты: delegatecall, UUPS и цена гибкости
- MEV и фронтраннинг: кто и как зарабатывает на порядке транзакций
- Foundry для начинающих: компиляция, тесты, фаззинг и локальная сеть
- Аудиты и баг-баунти: как проверяют смарт-контракты и как читать отчёт
Готовы попробовать?
Создайте первую сделку за пару минут. Деньги блокируются в смарт-контракте и выплачиваются по правилам, которые видны заранее.

