Розробка NFT з royalties

Проєктуємо та розробляємо блокчейн-рішення повного циклу: від архітектури смарт-контрактів до запуску DeFi-протоколів, NFT-маркетплейсів та криптобірж. Аудит безпеки, токеноміка, інтеграція з наявною інфраструктурою.
Показано 1 з 1Усі 1306 послуг
Розробка NFT з royalties
Простий
~2-3 дні
Часті запитання

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

Етапи блокчейн-розробки

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1306
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1218
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    920
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1147
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    610
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    885

Розробка NFT з роялті

У 2022-2023 роках маркетплейси почали роблять роялті опціональними — Blur запропонував нульові комісії, частина аудиторії OpenSea перейшла туди. Творці колекцій втратили мільйони. Це привело до двох таборів: ті, хто впроваджує on-chain enforcement роялті, та ті, хто покладаються на добру волю маркетплейсів. Вибір між ними — не технічний, а продуктовий. Ми реалізуємо обидва підходи.

ERC-2981 як базовий стандарт

ERC-2981 — це сигнальний стандарт. Контракт оголошує royaltyInfo(tokenId, salePrice), маркетплейс читає та (опціонально) платить. Blur може ігнорувати. OpenSea поважає. Magic Eden — залежить.

Реалізація через 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 маркетплейс не побачить підтримку ERC-2981 при перевірці ERC-165.

Operator Filter: On-chain Enforcement

Якщо роялті комерційно важливі — потрібен operator filter. Ідея: контракт перевіряє кожен transferFrom та safeTransferFrom, дозволяючи transfer лише через затверджені маркетплейси, які чесно платять роялті.

OpenSea запропонував OperatorFilterRegistry у 2022 році:

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 перевіряє адресу оператора в OperatorFilterRegistry. Blur спочатку був заблокований, потім доданий після переговорів.

Компромісс: operator filter захищає роялті, але обмежує ліквідність — користувачи не можуть торгувати на незатверджених платформах. Для деяких колекцій це неприйнятно.

Кастомна логіка Enforcement роялті

Незалежність від реєстру 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");
}

Менш гнучко, але незалежно від зовнішніх реєстрів.

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 паттерн — без ризику reentrancy при автоматичній дистрибуції.

Типові помилки

Забутий supportsInterface — маркетплейс не бачить ERC-2981. Роялті на нульову адресу при address(0) receiver — платежі йдуть в нікуди. Занадто високі роялті (>10%) зменшують обсяг торгів. Немає способу оновити receiver — творець не може змінити адресу гаманця.

Щоб receiver був оновлювальним, додайте updateDefaultRoyalty() з onlyOwner:

function updateDefaultRoyalty(address receiver, uint96 feeNumerator) 
    external onlyOwner {
    _setDefaultRoyalty(receiver, feeNumerator);
}

Оцінка часу

NFT контракт з ERC-2981 роялті та PaymentSplitter — 2-3 дні. З operator filter та кастомною логікою enforcement — 3-4 дні.