Розробка ERC-721 токена (NFT)
ERC-721 — стандарт, який знає кожен, але пишуть правильно небагато. Типова історія: проект деплоює колекцію, через місяць виясняється, що royalty не виплачується на OpenSea (тому що не реалізований EIP-2981), metadata завантажується з централізованого сервера (який впав), а mint через _safeMint замість _mint дозволяє повторний reentrancy через onERC721Received у кастомних контрактах-отримувачів. Правильний ERC-721 — це не просто відповідність інтерфейсу, це розуміння, як маркетплейси, гаманці та агрегатори взаємодіють з контрактом.
Що повинно бути в контракті
Базова імплементація через OpenZeppelin
Відправна точка — ERC721 з OpenZeppelin 5.x. Не пишемо стандарт з нуля: OZ пройшов десятки аудитів, будь-яка кастомна реалізація додає ризик без очевидної вигоди. Розширюємо через наслідування:
contract MyNFT is ERC721, ERC721Enumerable, ERC721URIStorage, ERC2981, Ownable {
uint256 private _nextTokenId;
uint256 public constant MAX_SUPPLY = 10000;
constructor(address initialOwner)
ERC721("My Collection", "MYC")
Ownable(initialOwner) {}
}
ERC721Enumerable потрібен, якщо контракт повинен повертати список токенів власника (tokenOfOwnerByIndex). Додає ~20K gas до кожного transfer через додаткові storage операції. Якщо маркетплейс не вимагає — краще обійтись без нього та читати дані через The Graph.
ERC721URIStorage дозволяє зберігати окремий URI для кожного токена. Альтернатива — baseURI + tokenId паттерн, де всі токени використовують єдиний базовий шлях. Другий варіант дешевше в gas при mint.
EIP-2981: royalty на рівні контракту
// Налаштування роялті через ERC2981
_setDefaultRoyalty(royaltyReceiver, royaltyBps); // bps: 500 = 5%
OpenSea та більшість сучасних маркетплейсів читають royaltyInfo() з EIP-2981. Старі маркетплейси використовували off-chain конфіг через Operator Filter Registry — цей підхід застарів. Реалізація EIP-2981 — мінімальний стандарт для будь-якої колекції 2024+.
Важливо: royaltyBps не забезпечується on-chain примусово — це інформаційний стандарт. Маркетплейс може його ігнорувати. Для примусового royalty потрібні кастомні transfer hooks (EIP-2981 + transfer обмеження через operator whitelist).
Метадані та зберігання
URI токена повертає JSON з полями name, description, image, attributes. Де зберігати:
IPFS — децентралізовано, дешево, але немає гарантії доступності без pinning. Використовуємо Pinata або nft.storage для pinning. CID фіксується в контракті, metadata не змінюється.
Arweave — постійне зберігання за розовою платою. Один платіж при завантаженні, дані зберігаються перманентно (~200 років за розрахунками протоколу). Дорожче IPFS на старті, але надійніше для довгострокових колекцій.
On-chain — metadata в base64 прямо в tokenURI(). Повністю децентралізовано, але дорого по gas при mint. Підходить для generative art з малими колекціями (< 1000 токенів).
Централізований сервер — тільки для pre-reveal фази. Після reveal URI повинен переключитись на IPFS. Реалізуємо через флаг revealed та два baseURI.
Gas-оптимізація mint
Стандартний _safeMint дорожче _mint через перевірку IERC721Receiver на контрактних адресах. Якщо mint призначений тільки для EOA — використовуємо _mint. Якщо потрібна підтримка контрактних гаманців (multisig) — _safeMint з явним reentrancy guard.
Для batch mint використовуємо ERC-721A (Azuki) замість стандартного OZ ERC-721. ERC-721A зберігає дані власника тільки при першому mint у batch, наступні токени дедуцюються — економія до 70% gas при mint 10+ токенів. Trade-off: transfer першого токена в batch трохи дорожче через lazy initialization.
Процес роботи
Аналітика (0.5-1 день). Supply, mint механіка (public/whitelist/free), royalty, потребуються ли Enumerable та URIStorage, чейн деплою.
Розробка (1-2 дні). Контракт + Foundry тести + скрипт деплою з верифікацією.
Деплой. Testnet (Sepolia), потім mainnet. Верифікація на Etherscan автоматично через Foundry.
Базовий ERC-721 з royalty та IPFS metadata — 2-3 дні. З вайтлістом (Merkle proof), reveal механікою та кастомним mint сайтом — 5-7 днів. Вартість розраховується індивідуально.







