Разработка метаданных 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 дней в зависимости от сложности арта.







