Принудительные роялти в NFT: от ERC-2981 до кастомного whitelist
Вы запустили коллекцию из 10 000 NFT, вложили миллионы в арт и маркетинг, а через месяц видите — ваши роялти не выплачиваются. Раньше маркетплейсы делали это автоматически, но потом Blur ввёл нулевые комиссии, и часть платформ перестала чтить EIP-2981. Создатели потеряли миллионы. Выбор между принудительным ончейн-принуждением и добровольными выплатами стал продуктовым, а не техническим. Мы реализуем оба подхода, добавляем кастомную логику и гарантируем, что роялти дойдут до вас. При объёме вторичных торгов в 100 ETH роялти в 7,5% принесут 7,5 ETH — но только если они защищены принудительно. Получите консультацию — напишите нам, чтобы начать с бесплатного аудита.
Как обеспечить принудительные выплаты роялти?
ERC-2981: базовый, но необязательный
ERC-2981 — сигнальный стандарт. Контракт объявляет royaltyInfo(tokenId, salePrice), маркетплейс читает и (опционально) выплачивает. Blur может игнорировать. OpenSea чтит. Magic Eden — depends.
Реализация через OpenZeppelin занимает 10 строк:
import "@openzeppelin/contracts/token/common/ERC2981.sol"; contract MyCollection is ERC721, ERC2981 { constructor(address royaltyReceiver) ERC721("Collection", "COL") { _setDefaultRoyalty(royaltyReceiver, 750); // 7.5% } function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC2981) returns (bool) { return super.supportsInterface(interfaceId); } } Без supportsInterface override маркетплейс не увидит ERC-2981 поддержку при ERC-165 проверке. Это типичная ошибка, которую мы встречали в 10+ аудитах.
Operator Filter: принудительное взыскание
Если роялти важны коммерчески — нужен operator filter. Идея: контракт проверяет каждый transferFrom и safeTransferFrom, разрешает transfer только через апрувнутые маркетплейсы, которые честно выплачивают роялти. Operator Filter работает в 2-3 раза надёжнее чистого ERC-2981 по гарантии выплат.
OpenSea предложил OperatorFilterRegistry.
import {DefaultOperatorFilterer} from "operator-filter-registry/src/DefaultOperatorFilterer.sol"; contract MyCollection is ERC721, ERC2981, DefaultOperatorFilterer { function transferFrom(address from, address to, uint256 tokenId) public override onlyAllowedOperator(from) { super.transferFrom(from, to, tokenId); } function safeTransferFrom(address from, address to, uint256 tokenId) public override onlyAllowedOperatorApproval(from) { super.safeTransferFrom(from, to, tokenId); } } onlyAllowedOperator проверяет адрес оператора в реестре. Blur был изначально заблокирован, потом добавлен после переговоров.
Компромисс: operator filter защищает роялти, но ограничивает ликвидность — пользователи не могут торговать на неодобренных платформах. Для некоторых коллекций это неприемлемо.
Почему ERC-2981 недостаточен для защиты роялти?
ERC-2981 без фильтра — это честное слово. Operator filter даёт ончейн-гарантию. Если ваш проект рассчитан на долгосрочные продажи и планирует зарабатывать на роялти, фильтр окупается. Для арт-коллекций с высокой вторичной активностью мы рекомендуем именно его. Наш опыт: более 20 проектов использовали фильтр и увеличили доход от роялти на 30-50% по сравнению с чистыми ERC-2981.
Как настроить собственный whitelist маркетплейсов?
Независимость от OpenSea реестра — через кастомную логику. Подход: разрешаем transfer только если он инициирован через whitelist контрактов (маркетплейсы, которые явно интегрировали наш роялти механизм), или если это wallet-to-wallet transfer (не через маркетплейс).
mapping(address => bool) public approvedMarketplaces; function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal override { // Разрешаем прямые трансферы (не через маркетплейс) if (from == tx.origin || to == tx.origin) return; // Проверяем, что маркетплейс одобрен require(approvedMarketplaces[msg.sender], "Marketplace not approved"); } Это менее гибко, но независимо от внешних реестров. При изменении рыночной ситуации вы сами добавляете или удаляете платформы без ожидания обновления реестра OpenSea.
Splitter для команд
Если роялти делятся между несколькими адресами, ставим receiver в ERC-2981 на PaymentSplitter:
address[] memory payees = [founder, artist, treasury]; uint256[] memory shares = [50, 30, 20]; PaymentSplitter splitter = new PaymentSplitter(payees, shares); _setDefaultRoyalty(address(splitter), 500); // 5% роялти на сплиттер Каждый получатель вызывает splitter.release(token) чтобы забрать накопленные средства. Pull pattern — нет риска reentrancy при автоматической рассылке.
Сравнение подходов к роялти
| Подход | Принуждение | Зависимость | Сложность | Ликвидность |
|---|---|---|---|---|
| ERC-2981 | Нет (опционально) | от маркетплейса | Низкая | Высокая |
| Operator Filter | Да | от реестра OpenSea | Средняя | Ограничена |
| Кастомный whitelist | Да | от вашего контракта | Высокая | Умеренная |
Типичные ошибки и их последствия
| Ошибка | Последствие | Как избежать |
|---|---|---|
Забытый supportsInterface |
Маркетплейс не видит ERC-2981 | Всегда override |
| Роялти на нулевой адрес | Выплаты уходят в никуда | Проверять address(0) |
| Слишком высокий % | Падение торгового объёма | 5-7.5% оптимально |
| Отсутствие функции обновления receiver | Нельзя сменить кошелёк | Добавить updateDefaultRoyalty |
Для обновляемого receiver добавляем updateDefaultRoyalty() с onlyOwner:
function updateDefaultRoyalty(address receiver, uint96 feeNumerator) external onlyOwner { _setDefaultRoyalty(receiver, feeNumerator); } Пошаговая инструкция по внедрению Operator Filter
- Установите пакет
operator-filter-registryчерез npm или Foundry. - Унаследуйте контракт от
DefaultOperatorFilterer. - Добавьте модификаторы
onlyAllowedOperatorиonlyAllowedOperatorApprovalв функции перевода. - Протестируйте на тестовой сети Rinkeby или Goerli.
- Разверните на mainnet и проверьте, что трансферы через одобренные площадки проходят.
Что входит в разработку NFT с роялти под ключ
- Смарт-контракт с ERC-2981 или operator filter
- Кастомная enforcement логика (по необходимости)
- PaymentSplitter для распределения роялти
- Скрипты для минтинга и взаимодействия
- Документация по развёртыванию и обновлению
- Поддержка в течение 30 дней после деплоя
Ориентиры по срокам
NFT контракт с ERC-2981 роялти и PaymentSplitter — 2-3 дня. С operator filter и кастомной enforcement логикой — 3-4 дня. Оценим ваш проект в течение дня. Готовы обсудить детали? Свяжитесь с нами — начнём с бесплатного аудита вашего текущего контракта.







