Разработка NFT с принудительными роялти: контракты и защита

Принудительные роялти в NFT: от ERC-2981 до кастомного whitelist Вы запустили коллекцию из 10 000 NFT, вложили миллионы в арт и маркетинг, а через месяц видите — ваши роялти не выплачиваются. Раньше маркетплейсы делали это автоматически, но потом Blur ввёл нулевые комиссии, и часть платформ перес

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

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

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

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

Принудительные роялти в 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

  1. Установите пакет operator-filter-registry через npm или Foundry.
  2. Унаследуйте контракт от DefaultOperatorFilterer.
  3. Добавьте модификаторы onlyAllowedOperator и onlyAllowedOperatorApproval в функции перевода.
  4. Протестируйте на тестовой сети Rinkeby или Goerli.
  5. Разверните на mainnet и проверьте, что трансферы через одобренные площадки проходят.

Что входит в разработку NFT с роялти под ключ

  • Смарт-контракт с ERC-2981 или operator filter
  • Кастомная enforcement логика (по необходимости)
  • PaymentSplitter для распределения роялти
  • Скрипты для минтинга и взаимодействия
  • Документация по развёртыванию и обновлению
  • Поддержка в течение 30 дней после деплоя

Ориентиры по срокам

NFT контракт с ERC-2981 роялти и PaymentSplitter — 2-3 дня. С operator filter и кастомной enforcement логикой — 3-4 дня. Оценим ваш проект в течение дня. Готовы обсудить детали? Свяжитесь с нами — начнём с бесплатного аудита вашего текущего контракта.