Разработка reveal-механики NFT

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1 услугВсе 1306 услуг
Разработка reveal-механики NFT
Средняя
~2-3 рабочих дня
Часто задаваемые вопросы
Направления блокчейн-разработки
Этапы блокчейн-разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1258
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1170
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    873
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1092
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    563
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    830

Разработка reveal-механики NFT

Reveal — момент, когда NFT «открывается»: placeholder изображение заменяется финальным. Технически это обновление baseURI в контракте или переключение логики tokenURI. Проблема не в реализации reveal как такового — проблема в честности: кто знал финальный mapping tokenId → trait до открытия, и мог ли он это использовать для снайпинга редких токенов.

Почему случайность reveal — это задача безопасности

Проблема предсказуемости

Если команда проекта хранит файлы метаданных заранее (например, 1.json, 2.json, ..., 10000.json), то до публичного reveal они знают, какой tokenId получает какой trait. Простой сценарий: команда минтит во время presale, зная, что токен #777 — легендарный. Или — хуже — продаёт эту информацию.

Проблема существует даже если команда честная: файлы метаданных часто загружаются на IPFS до launch. Внимательный исследователь может обнаружить CID, получить доступ к метаданным через gateway и снайпить редкие токены на aftermarket сразу после reveal.

Уязвимость on-chain randomness

Ранние проекты использовали blockhash(block.number - 1) или keccak256(abi.encodePacked(block.timestamp, msg.sender)) как источник случайности для reveal. Обе реализации предсказуемы. Майнер (на PoW) или валидатор (на PoS) контролирует block.timestamp в пределах нескольких секунд. Атакующий контракт может проверить, какой trait он получит, и revert если невыгодно — это называется reroll attack.

Любой on-chain источник случайности уязвим, потому что результат детерминирован и виден заранее тому, кто строит блок.

Chainlink VRF как стандарт честного reveal

Chainlink VRF (Verifiable Random Function) v2 — единственный production-ready способ получить верифицируемо случайное число on-chain. Схема:

  1. Контракт запрашивает randomness через requestRandomWords(keyHash, subId, confirmations, callbackGasLimit, numWords)
  2. Chainlink oracle генерирует случайное число + криптографическое доказательство
  3. Через 1-3 блока вызывается fulfillRandomWords(requestId, randomWords) в нашем контракте
  4. Контракт сохраняет revealOffset = randomWords[0] % totalSupply

После reveal: tokenURI(tokenId) возвращает метаданные для (tokenId + revealOffset) % totalSupply. Команда не знает revealOffset до получения ответа от VRF — честность гарантирована криптографически.

Subscription vs Direct Funding

VRF v2 поддерживает две модели оплаты. Subscription — пополняем баланс в LINK на subscriptionId, несколько контрактов могут использовать одну подписку. Direct Funding — каждый запрос оплачивается отдельно. Для reveal используем Subscription: один запрос на весь проект, стоимость 0.1-0.2 LINK (Ethereum) или меньше на L2.

Важно: callbackGasLimit должен покрывать выполнение fulfillRandomWords. Если газ слишком низкий — callback не выполнится, randomness будет потеряна. Для simple reveal достаточно 100k gas.

Альтернативные подходы

Commit-reveal от команды. Команда публикует хэш keccak256(secret) перед минтингом, после окончания раскрывает secret. Offset = uint256(keccak256(secret)) % totalSupply. Честно, если команда не может изменить secret после публикации хэша. Минус — trust assumption на команду.

Delayed upload метаданных. Контракт деплоится без baseURI. После окончания минта команда генерирует mapping, загружает на IPFS, устанавливает baseURI. Технически честно, но непрозрачно для пользователей — нет on-chain гарантии.

Provenance hash. Стандарт, popularized Bored Ape Yacht Club: до деплоя публикуется хэш от concatenation всех изображений в финальном порядке. Пользователи могут верифицировать, что изображения не менялись после публикации хэша. Не решает проблему предсказуемости assignment, но фиксирует контент.

Реализация в контракте

uint256 public revealOffset;
bool public revealed;
string public unrevealedURI;

function tokenURI(uint256 tokenId) public view override returns (string memory) {
    if (!revealed) return unrevealedURI;
    uint256 revealedId = (tokenId + revealOffset) % totalSupply();
    return string(abi.encodePacked(baseURI, revealedId.toString(), ".json"));
}

// Вызывается только один раз после окончания минта
function requestReveal() external onlyOwner {
    require(!revealed, "Already revealed");
    // Chainlink VRF v2 request
    COORDINATOR.requestRandomWords(keyHash, subId, 3, 100000, 1);
}

function fulfillRandomWords(uint256, uint256[] memory randomWords) internal override {
    revealOffset = randomWords[0] % totalSupply();
    revealed = true;
    emit Revealed(revealOffset);
}

Процесс работы

Конфигурация VRF (1 день). Регистрация subscription на vrf.chain.link, пополнение LINK, добавление consumer контракта.

Разработка и тесты (1-2 дня). Контракт с VRF интеграцией. Foundry mock для тестирования fulfillRandomWords локально — используем VRFCoordinatorV2Mock из Chainlink library.

Тестирование на Sepolia (1 день). VRF работает на всех основных testnet. Верифицируем полный flow: минт → requestReveal → ожидание callback → проверка tokenURI.

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

Reveal механика как отдельный компонент к существующему контракту — 2-4 дня. В составе полной разработки коллекции — входит в основной scope.