Разработка PFP NFT-коллекции
PFP (Profile Picture) коллекция — 10 000 уникальных изображений, сгенерированных из слоёв traits: фон, тело, одежда, голова, аксессуары. Технически это конкретный пайплайн: генератор изображений + алгоритм распределения рарити + ERC-721 контракт с reveal механикой + IPFS-хостинг метаданных. Задача не в том, чтобы «сделать NFT» — задача в том, чтобы сделать правильно с точки зрения безопасности минтинга и честного распределения рарити.
Где ломаются PFP-проекты
Предсказуемый rarity reveal
Самая распространённая ошибка: метаданные открываются сразу при минте. Tokenizer получает #4521, идёт на rarity.tools — видит, что это топ-1% по редкости. Опытные игроки сканируют транзакции в реальном времени и front-run минт редких токенов, предугадывая token ID по порядковому номеру транзакции.
Решение — delayed reveal: при минте все токены показывают placeholder изображение. После окончания минта владелец проекта устанавливает baseURI с реальными метаданными через setBaseURI. Но это порождает другую проблему: команда знает финальный mapping tokenId → trait заранее и может зарезервировать редкие токены.
Честный reveal — через Chainlink VRF. После окончания минта запрашиваем случайное число у VRF, используем его как offset: tokenId #5000 получает метаданные из файла (5000 + offset) % totalSupply. Ни команда, ни минтеры не знают финальный mapping до получения randomness.
Неравномерное распределение при генерации
Наивный генератор выбирает каждый trait случайно с весами — но не проверяет комбинации. В результате «редкий» trait может случайно встречаться чаще ожидаемого из-за корреляции с популярными базовыми traits. Правильный подход: задаём точное количество каждого trait, генератор shuffle'ит и распределяет детерминированно, проверяет итоговую rarity таблицу перед финализацией.
Публичный минт без защиты от ботов
Без защиты первые 1000 токенов уходят MEV-ботам за один блок. Стандартные механизмы:
-
Merkle proof whitelist: только адреса из списка могут минтить.
MerkleProof.verifyиз OpenZeppelin — дёшево по газу. - Commit-reveal: пользователь сначала фиксирует намерение минта (commit), через N блоков забирает токен (reveal). Предотвращает atomic snipe ботов.
-
Max per wallet через mapping — базовая защита. Обходится через контракты, если не добавить
require(tx.origin == msg.sender)или ERC-721Psi с packed ownership.
Как мы строим коллекцию
Генератор изображений
Python-скрипт на базе Pillow: загружает слои PNG с прозрачностью, компонует по приоритету, сохраняет итоговое изображение. Конфиг для каждого trait: {name, weight, files[]}. Генератор гарантирует уникальность через hash сгенерированных комбинаций, при коллизии повторяет генерацию.
Финальный шаг — validation скрипт: проверяет отсутствие дубликатов, соответствие весов ожидаемому распределению рарити (±2%), корректность всех файлов метаданных.
ERC-721 контракт
Базируемся на ERC-721A (от Azuki) — оптимизированная реализация, которая позволяет минтить несколько токенов за одну транзакцию с почти той же ценой газа, что и один токен. Оригинальный OpenZeppelin ERC-721 обновляет _owners mapping для каждого tokenId — это O(n) по газу при минте n токенов. ERC-721A использует lazy initialization: _owners обновляется только для первого токена в batch.
Экономия существенная: минт 5 ERC-721A токенов стоит ~120k gas против ~400k для стандартного ERC-721. На mainnet при 50 gwei это разница $20 vs $66 за транзакцию.
Дополнительные компоненты: EIP-2981 для роялти (поддерживается OpenSea, Blur, LooksRare), Ownable2Step вместо Ownable (защита от случайной передачи прав), ERC721Burnable если планируется механика сжигания.
Хранение метаданных
IPFS через Pinata или NFT.Storage. Структура: все изображения загружаются в один IPFS директорий, получаем CID директории. Метаданные JSON ссылаются на ipfs://{CID}/{tokenId}.png. baseURI в контракте — ipfs://{CID}/.
Формат метаданных по стандарту OpenSea:
{
"name": "Collection #1234",
"description": "...",
"image": "ipfs://Qm.../1234.png",
"attributes": [
{"trait_type": "Background", "value": "Blue"},
{"trait_type": "Eyes", "value": "Laser"}
]
}
trait_type и value должны точно совпадать с rarity таблицей — от этого зависит корректность отображения на OpenSea и rarity агрегаторах.
Процесс работы
Арт и слои (параллельно с разработкой). Принимаем от клиента: PNG слои с прозрачностью, конфиг весов trait'ов. Генерируем превью выборки из 100 токенов для согласования.
Генератор и rarity (2-3 дня). Финальная генерация 10K изображений, rarity таблица, валидация уникальности.
Загрузка на IPFS (1 день). Загрузка через Pinata API, получение CID.
Контракт и тесты (3-4 дня). ERC-721A + VRF reveal + whitelist. Foundry тесты: минт, reveal, transfer, royalty.
Деплой (1-2 дня). Testnet (Sepolia) → согласование → mainnet. Верификация на Etherscan. Настройка коллекции на OpenSea (description, royalties, banner).
Ориентиры по срокам
От получения арт-файлов до деплоя на mainnet — 1.5-2 недели. Отдельные механики (staking, merging токенов, on-chain генерация) добавляют 1-2 недели каждая.







