Разработка метаданных NFT (on-chain/off-chain)

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1 услугВсе 1306 услуг
Разработка метаданных NFT (on-chain/off-chain)
Простая
~2-3 рабочих дня
Часто задаваемые вопросы
Направления блокчейн-разработки
Этапы блокчейн-разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1221
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1163
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    855
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1056
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    561
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    828

Разработка метаданных NFT (on-chain/off-chain)

tokenURI() возвращает строку — URL или base64-encoded JSON. За этой простотой скрывается архитектурное решение, которое определит судьбу коллекции на годы вперёд. Метаданные на centralised IPFS gateway — это не «децентрализованные метаданные», это ссылка на сервер Pinata, который может исчезнуть. NFT с метаданными на контракте (on-chain) переживёт любой hosting.

On-chain vs. off-chain: реальные trade-offs

Полностью on-chain

Метаданные хранятся прямо в смарт-контракте. tokenURI() генерирует JSON и SVG в runtime через string concatenation:

function tokenURI(uint256 tokenId) public view override returns (string memory) {
    string memory json = Base64.encode(bytes(string(abi.encodePacked(
        '{"name":"Token #', Strings.toString(tokenId),
        '","description":"On-chain NFT","image":"data:image/svg+xml;base64,',
        Base64.encode(bytes(_generateSVG(tokenId))),
        '"}'
    ))));
    return string(abi.encodePacked("data:application/json;base64,", json));
}

Преимущество: полная постоянность, нет зависимости от внешних сервисов. Недостаток: газ на деплой растёт с размером SVG. Для простой генеративной коллекции (Loot, Nouns-стиль) это работает. Для фотографий — нет.

Хранение атрибутов в storage: маппинг tokenId → struct с trait values. Каждый атрибут — uint8 или bytes32 для экономии слотов. uint8 атрибуты пакуются по 32 в один storage slot.

IPFS off-chain

Стандартный подход для большинства коллекций. Метаданные загружаются в IPFS, tokenURI() возвращает ipfs://CID/tokenId.json. Критическое требование: не использовать HTTP gateway в URI.

Правильно: ipfs://QmHash/1.json Неправильно: https://ipfs.io/ipfs/QmHash/1.json

Второй вариант — это ссылка на конкретный HTTP сервер. Он может исчезнуть. Первый — контентный адрес, который работает с любым IPFS гейтвеем.

Для pinning — Pinata + Web3.Storage как backup. Для самых важных коллекций — Filecoin через NFT.Storage для долгосрочного хранения с cryptographic guarantee.

Reveal механизм

Pre-reveal: все токены показывают placeholder метаданные. Post-reveal: реальные метаданные раскрываются. Наивная реализация — owner просто меняет baseURI. Это централизованно и доверительно.

Схема commit-reveal на VRF: перед mintом owner коммитит хэш seed, после mint завершён — публикует seed и вызывает Chainlink VRF для получения случайного offset. Метаданные перемешиваются детерминированно через (tokenId + offset) % totalSupply. Никто не может знать заранее, какие traits достанутся конкретному токену.

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

function tokenURI(uint256 tokenId) public view override returns (string memory) {
    require(revealed, "Not revealed yet");
    uint256 metadataId = (tokenId + revealOffset) % maxSupply;
    return string(abi.encodePacked(baseURI, metadataId.toString(), ".json"));
}

Структура JSON метаданных

Стандарт OpenSea ERC-721 metadata:

{
  "name": "Token #1",
  "description": "Description text",
  "image": "ipfs://CID/1.png",
  "external_url": "https://project.xyz/token/1",
  "attributes": [
    {"trait_type": "Background", "value": "Blue"},
    {"trait_type": "Rarity", "value": "Legendary", "display_type": "boost_percentage", "max_value": 100}
  ]
}

display_type управляет отображением в OpenSea. Числовые атрибуты: "number" (просто число), "boost_percentage" (прогресс-бар), "boost_number" (модификатор), "date" (unix timestamp → дата).

Для ERC-1155 структура аналогична, но tokenURI принимает uint256 id и может использовать {id} placeholder в URI.

Инструменты и процесс

Генерация метаданных для большой коллекции — off-chain скрипт на TypeScript: загружает layers, генерирует комбинации с учётом рарити весов, создаёт JSON файлы и изображения, загружает batch в IPFS через Pinata API. Затем IPFS CID фиксируется в контракте.

Для on-chain SVG — TypeScript скрипт генерирует Solidity библиотеки со string constants для каждого trait. Размер контракта проверяется: лимит 24KB.

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

IPFS off-chain метаданные с reveal механизмом — 2-3 дня. On-chain SVG генерация для generative коллекции — 3-5 дней в зависимости от сложности арта.