10 000 уникальных CryptoPunks, 8 888 Azuki, 8 000 Milady — все эти коллекции построены на одном принципе: алгоритмическая комбинация слоёв трейтов с заданными вероятностями рождает уникальные изображения. Техническая сторона состоит из двух равноважных частей: генератор изображений и смарт-контракт минта. Ошибки на любом этапе — от неправильной матрицы совместимости до уязвимости в контракте — могут стоить тысяч долларов газа и репутации.
В этом обзоре речь пойдёт о разработке Ethereum коллекции: минтинг NFT, ERC-721A, conditional traits, Merkle tree whitelist, Dutch auction и IPFS метаданные. Наша команда занимается разработкой NFT-коллекций более 4 лет. За это время мы выпустили более 10 проектов на Ethereum, Polygon и Solana. Опыт позволяет гарантировать качество на каждом этапе: от генерации до деплоя. В этой статье подробно разберём технические аспекты создания генеративной коллекции: от генерации изображений до деплоя смарт-контракта. Вы узнаете, как избежать типовых ошибок и сэкономить на газе при batch mint.
Структура трейтов и рарити
Коллекция разбивается на слои (background, body, clothing, eyes, mouth, accessories). Каждый слой содержит варианты с заданными весами. Например, для background:
"background": [ { "name": "Золотой", "weight": 2 }, { "name": "Синий", "weight": 25 }, { "name": "Серый", "weight": 73 } ] Генератор случайно выбирает вариант пропорционально весам и комбинирует PNG-слои. Результат: 2% коллекции получают золотой фон, 73% — серый.
Ключевая проблема: при наивной реализации рарити нарушается из-за конфликтующих трейтов (например, скелет-персонаж не может носить обычную одежду). Реализуем conditional traits: матрица совместимости слоёв, которая исключает невалидные комбинации. При большом числе ограничений алгоритм может зациклиться — нужен backtracking с max-attempts.
Подробнее о conditional traits
Матрица совместимости задаётся как битовая маска: для каждого слоя перечислены разрешённые идентификаторы других слоёв. Генератор на Node.js последовательно выбирает вариант каждого слоя, проверяя совместимость с уже выбранными. Если зацикливание — увеличиваем max-attempts или перезапускаем генерацию с другого слоя.Инструмент: собственный генератор на Node.js с использованием sharp для compositing PNG-слоёв. sharp в 3–5 раз быстрее canvas-based решений — 10k изображений генерируются за 5–15 минут. Для анимированных коллекций (GIF/APNG) — ffmpeg через child process.
Metadata и стандарты
Каждый токен требует JSON метаданных формата OpenSea metadata standard:
{ "name": "Collection #1234", "description": "...", "image": "ipfs://Qm.../1234.png", "attributes": [ { "trait_type": "Background", "value": "Золотой" }, { "trait_type": "Eyes", "value": "Лазерные" } ] } Поле image должно указывать на IPFS или Arweave. Централизованный сервер — смерть коллекции при закрытии. Загружаем через Pinata или NFT.Storage, получаем CID, формируем baseURI вида ipfs://QmXxx/. Все метаданные на IPFS загружаются автоматически.
Смарт-контракт: ERC-721 и механики минта
Почему стоит использовать ERC-721A?
Базовая структура на OpenZeppelin:
contract MyCollection is ERC721A, Ownable, ReentrancyGuard { uint256 public constant MAX_SUPPLY = 10000; uint256 public constant MAX_PER_WALLET = 5; string private _baseTokenURI; mapping(address => uint256) public mintedPerWallet; } Используем ERC-721A (Azuki's implementation) вместо стандартного ERC-721: batch mint 5 токенов в ERC-721A потребляет ~50k газа против ~250k в классической реализации. Разница ощутима при 10k коллекции на Ethereum mainnet. Экономия газа достигает 80% при массовом минте.
| Метрика | ERC-721 (OpenZeppelin) | ERC-721A (Azuki) |
|---|---|---|
| Газ на mint 1 токена | ~90k | ~50k |
| Газ на mint 5 токенов | ~250k | ~50k |
| Поддержка burn | Да | Да |
| Аудит | Множество аудитов | Аудирован (Azuki) |
Механики минта
Public mint — открыт для всех, часто с ограничением per wallet. Защита: require(mintedPerWallet[msg.sender] + quantity <= MAX_PER_WALLET). Проблема с контрактами: msg.sender — контракт, обходит лимит. Добавляем require(msg.sender == tx.origin) — но это ломает Safe/AA wallets. Компромисс: проверка msg.sender == tx.origin только в период mint, снимается после.
Whitelist mint — Merkle tree proof. Список адресов → корень Merkle tree → root хранится в контракте. Пользователь предоставляет proof (массив хешей), контракт верифицирует через MerkleProof.verify() из OpenZeppelin. Proof генерируется off-chain через merkletreejs, публикуется на фронтенде.
Dutch auction mint — цена начинается высокой и падает каждый N минут до минимума. Текущую цену считаем через startPrice - (elapsedTime / step) * priceDecrement. Пользователь платит текущую цену, избыток ETH возвращается в той же транзакции.
| Механика | Доступ | Цена | Gas cost | Сложность реализации |
|---|---|---|---|---|
| Public mint | Все | Фиксированная | Низкая | Низкая |
| Whitelist mint | По списку | Фиксированная или скидка | Средняя | Средняя |
| Dutch auction | Все | Динамическая (падающая) | Средняя | Высокая |
Как защитить коллекцию от снайперов?
Reveal механика — премиальные коллекции не раскрывают трейты до окончания продажи (anti-snipe). До reveal: tokenURI() возвращает один общий placeholder. После reveal: owner вызывает setBaseURI(ipfsCID) и все токены мгновенно показывают финальные изображения.
Более честная механика: Chainlink VRF для случайного seed. Контракт запрашивает random через requestRandomWords(), получает ответ в fulfillRandomWords(), записывает seed. Все tokenId рандомно перемешиваются относительно seed — нельзя угадать трейты даже зная порядок минта.
Royalties и маркетплейсы
ERC-2981 — стандарт on-chain royalties. Маркетплейсы, поддерживающие стандарт (Blur при включённой опции, OpenSea, Rarible), автоматически читают royaltyInfo(tokenId, salePrice) и удерживают процент. Добавляется через ERC2981 mixin из OpenZeppelin.
Для принудительного применения роялти: OperatorFilterRegistry (Blur/OpenSea подход) блокирует трансферы через контракты маркетплейсов, которые не соблюдают royalties. Но это спорная механика — ограничивает ликвидность. Решение: переменный флаг royaltiesEnforced, который owner может отключить.
Процесс разработки: пошагово
- Подготовка ассетов — слои в PNG с прозрачностью, таблица рарити, матрица несовместимостей. Зависит от художника.
- Генератор и metadata (2–4 дня) — Node.js генератор, batch генерация коллекции, JSON metadata, загрузка на IPFS через Pinata API.
- Смарт-контракт (3–5 дней) — ERC-721A базис, механики минта (public + whitelist + dutch auction в зависимости от требований), тесты в Foundry: supply limits, per-wallet limits, Merkle proof, refund при dutch auction.
- Frontend минт-сайт (3–5 дней) — React + wagmi + viem, подключение MetaMask/WalletConnect, wallet connect, рарити трекер.
- Деплой — Testnet (Sepolia) → mainnet. Верификация контракта на Etherscan.
Выбор механики минта
Для экономии газа и простоты — public mint с ERC-721A. Если важны контроль доступа и премиальные сценарии — комбинация whitelist + dutch auction. Мы помогаем подобрать оптимальный вариант под вашу коллекцию и целевую аудиторию.
Что входит в разработку под ключ
- Исходный код генератора изображений с конфигурацией трейтов
- Смарт-контракты с выбранными механиками минта (протестированы на testnet)
- Метаданные всех токенов (загружены на IPFS/Arweave)
- Фронтенд минт-сайта с подключением кошельков
- Документация по управлению коллекцией (reveal, проверка контракта)
- Поддержка при деплое на mainnet
Полный цикл от готовых ассетов до деплоя на mainnet занимает 1.5–2 недели. Стоимость рассчитывается индивидуально в зависимости от механик минта и требований к фронтенду. Получите консультацию по вашей коллекции — мы поможем подобрать оптимальные механики минта и рассчитаем бюджет. Свяжитесь с нами для обсуждения вашего проекта.







