Розробка NFT-мінтинг сторінки
Колекція з 10 000 токенів, запуск через 48 годин, 500 осіб одночасно — і половина йде через гальмуючу сторінку. Ми часто бачимо таку помилку: мінтинг-сторінка написана за вечір, кнопка «Mint», MetaMask підключення, все просто. У момент дропу MetaMask не встигає, транзакції зависають, whitelist не верифікується, прогрес-бар показує 0 навіть після 200 замінених NFT. Це не проблема хайпу — це проблема frontend-архітектури під навантаження. Розробка надійної мінтинг-сторінки потребує глибокого розуміння Web3-стеку. Команда з 7+ річним досвідом і 30+ реалізованими проєктами (обсягом понад 2000 ETH) знає, як це зробити.
Які компоненти критичні для мінтинг-сторінки?
Підключення гаманця та chain management. Wagmi v2 + viem — поточний стандарт для Web3 React додатків. useConnect, useAccount, useNetwork хуки покривають базові сценарії. Критичний момент — перевірка та перемикання мережі. Якщо користувач підключений до Ethereum, а мінтинг на Base, потрібен useSwitchChain з автоматичним запитом. Якщо відмовив — показуємо блокуюче попередження.
const { switchChain } = useSwitchChain()
const { chain } = useAccount()
if (chain?.id !== TARGET_CHAIN_ID) {
return <SwitchNetworkPrompt onSwitch={() => switchChain({ chainId: TARGET_CHAIN_ID })} />
}
Як ми вирішуємо проблему верифікації whitelist?
Два підходи: on-chain mapping та Merkle proof. On-chain mapping (mapping(address => bool) public whitelist) — дорого. Додати 5000 адрес у whitelist = 5000 транзакцій (до 1 ETH на mainnet). Merkle proof — стандарт для великих whitelist. Root зберігається on-chain (один bytes32), доказ для кожного адресу — off-chain, передається при мінтингу. Налаштування start коштує в 10 разів дешевше — одна транзакція замість тисяч. Frontend отримує proof через API або зберігає в публічному JSON.
Верифікація на frontend до відправки транзакції:
import { MerkleTree } from 'merkletreejs'
import { keccak256 } from 'viem'
const proof = merkleTree.getHexProof(keccak256(address))
const isValid = merkleTree.verify(proof, keccak256(address), merkleRoot)
Важливо: верифікація на frontend — тільки для UX. Фінальна перевірка — у смарт-контракті. Ніколи не довіряй frontend-верифікації.
Порівняння методів whitelist
| Метод |
Вартість setup |
Gas при мінтингу |
Масштабованість |
| On-chain mapping |
Висока (до 1 ETH на 5000 адрес) |
Середня |
Обмежена block gas limit |
| Merkle proof |
Мінімальна (одна транзакція) |
Низька |
Практично необмежена |
Merkle proof знижує витрати на setup з $10,000 (on-chain) до $100 — економія 99%. Детальніше в Wikipedia.
Чому реальний прогрес-бар потребує WebSocket?
Проблема stale data. totalSupply() змінюється з кожним заміненим NFT. Якщо читати через useReadContract з дефолтним polling — дані застарілі на 1-3 блоки. На гарячому дропі це означає, що прогрес-бар бреше.
Рішення: WebSocket підписка на Transfer події контракту. useWatchContractEvent з wagmi оновлює дані в 50 разів швидше, ніж polling, і не витрачає зайві RPC-запити.
useWatchContractEvent({
address: CONTRACT_ADDRESS,
abi: NFT_ABI,
eventName: 'Transfer',
onLogs: (logs) => {
const mints = logs.filter(log => log.args.from === zeroAddress)
setMintedCount(prev => prev + mints.length)
}
})
Це дає realtime оновлення без polling.
Стани транзакції
Користувач натиснув «Mint» — потрібно показати мінімум 4 стани явно:
| Стан |
Індикатор |
Дія |
| Awaiting signature |
Spinner + 'Підпишіть транзакцію' |
Очікування підтвердження |
| Transaction pending |
Spinner + transaction hash на Etherscan |
Показати посилання |
| Confirmed |
Зелений чек + NFT preview |
Показати NFT |
| Failed |
Червоний хрест + user-friendly помилка |
Запропонувати retry |
useWriteContract + useWaitForTransactionReceipt з wagmi покривають всі стани через isPending, isLoading, isSuccess, isError. Типова помилка: показувати spinner без transaction hash. Користувач не знає, чи пройшла транзакція, закриває вкладку і мінтить знову. Завжди показуй transaction hash щойно він з'явився.
Обробка помилок з контракту
Revert messages з Solidity custom errors потрібно декодувати. viem робить це автоматично, якщо в ABI є error definitions. Але користувачу не можна показувати технічне повідомлення на кшталт ERC721: transfer to non ERC721Receiver implementer. Маппінг помилок у user-friendly текст:
-
MaxSupplyReached → «Всі NFT вже замінені»
-
NotWhitelisted → «Ваш адрес не в whitelist»
-
MintingPaused → «Мінтинг тимчасово призупинено»
-
InsufficientFunds → «Недостатньо коштів»
Продуктивність під навантаження
У момент дропу сотні користувачів одночасно звертаються до RPC-провайдера. Публічні RPC (Infura free tier) мають rate limits. Рішення: Alchemy або QuickNode з платним планом + кешування статичних даних (totalSupply, mintPrice, whitelistRoot) у власному backend з TTL 2-5 секунд. Merkle proof-и для whitelist віддаємо через CDN (Cloudflare) — sub-50ms відповідь без навантаження на сервер.
Що входить у розробку
- Документація: опис архітектури, інструкція з деплою, специфікація контрактів.
- Доступи до репозиторію, тестової мережі та інструментів моніторингу (Tenderly, Etherscan).
- Навчання команди: 1-2 години онлайн-сесія з управління мінтинг-сторінкою.
- Підтримка після запуску: 2 тижні моніторингу та виправлення помилок.
Процес розробки
-
Розробка (3-4 дні). Next.js + wagmi + viem. Компоненти: wallet connector, mint button з усіма станами, progress bar з WebSocket, whitelist checker.
-
Інтеграція з контрактом (1 день). ABI підключення, тестування на testnet (Sepolia), перевірка edge cases: wrong network, not whitelisted, sold out, paused.
-
Оптимізація (1 день). RPC кешування, CDN для proof-ів, gas estimate перед транзакцією.
Орієнтири за термінами
Стандартна мінтинг-сторінка з whitelist та прогрес-баром — 3-5 днів. Складна з фазами мінтингу (presale, public), multiple wallet types та кастомним дизайном — до 2 тижнів. Вартість розраховується після уточнення функціональних вимог та дизайну.
Типові помилки при розробці мінтинг-сторінки
- Не перевіряти мережу користувача – мінтинг в іншій мережі провалиться.
- Показувати тільки spinner без transaction hash – користувач не знає статус.
- Використовувати polling для totalSupply – дані stale, прогрес-бар бреше.
- Довіряти frontend-верифікації whitelist – потрібно перевіряти в контракті.
- Не кешувати Merkle proof – навантаження на backend при великому whitelist.
- Не обробляти додавання другого мінтингу з тим самим гаманцем – потрібно перевіряти володіння токеном.
Отримайте консультацію по вашому проєкту — зв'яжіться з нами. Ми оцінимо складність та терміни безкоштовно. Замовте розробку мінтинг-сторінки з гарантією стабільності під навантаженням до 1000 одночасних користувачів.
Наша команда: 7+ років досвіду, 30+ реалізованих проєктів із загальним обсягом понад 2000 ETH. Гарантуємо стабільну роботу та економію на gas за рахунок Merkle proof та кешування.
Чому розробка NFT маркетплейсів потребує комплексного підходу?
Ми бачимо, що на перший погляд NFT-контракт виглядає просто: ERC-721, mint(), IPFS для метаданих, і все. На практиці саме в цій «простоті» ховається більшість проблем — від ботів, які скуповують весь mint у першому блоці, до зламаних роялті на вторинному ринку. Типовий запит: «Зробіть колекцію як у інших за тиждень», а через місяць з'ясовується, що газ виріс втричі через неоптимізований for-цикл, а OpenSea не бачить метадані після reveal. Ми знаємо кожні з цих граблів і будуємо процеси так, щоб їх уникнути.
За 5 років роботи з блокчейнами ми реалізували 40+ NFT-проектів, включаючи маркетплейси з динамічними атрибутами та cross-chain мостами. Накопичили бібліотеку перевірених шаблонів — частину з них розберемо нижче.
Який стандарт вибрати: ERC-721 чи ERC-1155?
ERC-721 — кожен токен унікальний, один owner. Підходить для колекцій, де кожен NFT має індивідуальні атрибути та пряму прив'язку owner → tokenId.
ERC-1155 — multi-token стандарт: один контракт зберігає і fungible, і non-fungible токени. Використовує balanceOf(address, tokenId) замість ownerOf(tokenId). Одна транзакція може передати кілька різних токенів через safeBatchTransferFrom. Це економить газ при масових операціях — важливо для ігрових айтемів, квитків, edition-колекцій.
| Критерій |
ERC-721 |
ERC-1155 |
| Унікальність токена |
Кожен токен унікальний |
Один tokenId може мати кілька копій |
| Баланс користувача |
Тільки ownerOf (один) |
balanceOf(address, tokenId) |
| Газ на transfer |
~25 000 gas |
~18 000 gas (batch — ще нижче) |
| Batch operations |
Немає нативної підтримки |
safeBatchTransferFrom |
| Ідеальний сценарій |
Art-колекції, PFPs |
Ігри, квитки, editions |
Конкретний кейс: ігровий проект з 50 видами айтемів, кожен у тиражі 10 000. ERC-721 — 500 000 унікальних токенів, величезний overhead на маппінги. ERC-1155 — 50 tokenId, balanceOf на кожного гравця. Газ на transfer нижче в 2–3 рази, деплой контракту дешевший. Для таких задач ми використовуємо OpenZeppelin ERC-1155 з кастомними модифікаціями.
Метадані: on-chain vs IPFS vs centralized
Стандартний шлях — tokenURI() повертає посилання на JSON з полями name, description, image, attributes. Три варіанти зберігання:
-
Centralized server — найдешевший і гнучкий. Ризик: сервер падає, компанія закривається — NFT втрачає метадані. Не підходить для колекцій з претензією на довгострокову цінність.
-
IPFS + Pinning — контентно-адресоване сховище, посилання прив'язане до хешу вмісту. Pinata або NFT.Storage забезпечують pіннінг. Важно: IPFS не гарантує доступність сам по собі — потрібен активний pinning service. Якщо він закриється, дані можуть зникнути, якщо ніхто не зберігає копію.
-
On-chain metadata — base64-encoded SVG або JSON прямо в tokenURI. Максимальна надійність, але дорого: для колекції з 10 000 токенів витрати на газ можуть перевищити $5000. Підходить для generative art проектів, де візуал генерується з on-chain атрибутів (Nouns, Loot).
Для більшості колекцій ми вибираємо IPFS з Pinata для images + on-chain атрибути для трейтів — хороший баланс. Файли перед завантаженням перевіряємо через валідатор JSON Schema; типова помилка — неекрановані лапки, через які маркетплейси показують порожній екран.
Dynamic NFT: метадані, які змінюються
Dynamic NFT оновлює метадані у відповідь на зовнішні події — результати матчів, рівень персонажа, реальні дані через Chainlink. Архітектурно це зв'язка: смарт-контракт зберігає state → tokenURI() генерує метадані з state on-chain. Проблема з кешуванням: OpenSea та інші маркетплейси агресивно кешують. Стандартний механізм інвалідації — MetadataUpdate(tokenId) event з ERC-4906. OpenSea слухає цей event і скидає кеш. Без нього оновлені метадані можуть не відображатися тижнями.
Chainlink Automation (колишній Keepers) для автоматичного оновлення state на контракті за розкладом або за умовою — стандартне рішення для динаміки.
Як захистити mint від ботів?
Allowlist через merkle tree — стандарт. Список адрес хешується в merkle root, зберігається в контракті. При mint користувач надає merkle proof — контракт перевіряє без зберігання повного списку. Використовуємо OpenZeppelin MerkleProof library.
Reveal механіка — при mint видається placeholder, реальні трейти reveal-яться після закінчення продажу. Інакше боти можуть сканувати pending транзакції і снайперити рідкісні трейти через frontrunning. Але reveal вимагає commitment scheme — випадковий seed має бути зафіксований до mint або використовувати Chainlink VRF.
Chainlink VRF для чесної рандомізації трейтів. VRF request в момент mint → callback з verifiable random number → assign traits. Це додає ~2 транзакції та latency, але гарантує чесність. Посилання на Chainlink VRF v2.5.
Rate limiting — require(mintedPerWallet[msg.sender] < maxPerWallet). Не захищає від мульти-гаманців, але піднімає вартість атаки. Для преміум-проектів часто додаємо proof-of-work прямо в контракт (через EIP-2612 signatures).
Royalties: реальний стан ринку
ERC-2981 — on-chain стандарт роялті. Контракт повертає (recipient, amount) для будь-якої sale price через royaltyInfo(tokenId, salePrice). Маркетплейси опитують це при кожному продажу. Проблема: дотримання роялті — добровільне рішення маркетплейсу. Blur запустився з нульовими роялті, що викликало хвилю інших платформ. Зараз ситуація частково стабілізувалася: OpenSea підтримує ERC-2981, Blur додав опціональні.
Спроби enforce роялті on-chain через обмеження transfers тільки на approved маркетплейси (operator filtering) OpenSea пропонував через OperatorFilterRegistry. Це ламає composability — не можна передати NFT через кастомний контракт. Більшість серйозних проектів відмовилися від цього підходу. Для проектів, де роялті критичні, ми будуємо кастомний маркетплейс всередині екосистеми + incentive structure для користувачів торгувати саме там.
Lazy minting та gas-free mint
Gas-free mint через підпис: творець підписує voucher (tokenId, tokenURI, price, signature), покупець надає voucher в mint() — контракт верифікує підпис через ECDSA.recover() і минтить. Працює на OpenSea через їх Seaport протокол. Seaport — оптимізований контракт з мінімальним gas usage. Розуміння його механіки важливе при інтеграції custom marketplace логіки.
Стек для NFT-проектів
- Контракти: Solidity 0.8.x, OpenZeppelin ERC721Enumerable або ERC721A (Azuki) для gas-оптимізованого batch mint, ERC1155 від OpenZeppelin
- VRF та автоматизація: Chainlink VRF v2.5, Chainlink Automation
- Зберігання: Pinata (IPFS pinning), NFT.Storage, Arweave для постійного зберігання
- Маркетплейс: OpenSea Seaport protocol, кастомна інтеграція
- Фронтенд: wagmi v2 + viem, RainbowKit для wallet connection, React + TypeScript
Процес розробки
-
Проектування mint-механіки — allowlist, public sale, price curve (Dutch auction або фіксована), limits per wallet
-
Контракти — з Foundry fuzz-тестами на mint limits, merkle proof-верифікацію, royalty calculations
-
IPFS деплой — завантаження метаданих та images до reveal, піннінг на мінімум двох сервісах
-
Reveal — якщо використовується Chainlink VRF, тест на testnet обов'язковий: VRF subscription має бути funded LINK токенами
-
Маркетплейс-інтеграція — верифікація колекції на OpenSea, налаштування роялті, тест MetadataUpdate events
-
Деплой та моніторинг — Tenderly для відлову reentrancy, Etherscan API для верифікації контракту, налаштування оповіщень за подіями
Що входить в роботу (deliverables)
- Вихідний код смарт-контрактів (Solidity, Rust для Solana) з коментарями
- Тест-сьют (Foundry/Hardhat) з покриттям ≥90%
- Документація розгортання та інструкції з інтеграції
- Доступи до pinning-сервісів (Pinata/Pinfluence)
- Скрипти для генерації метаданих (Python/JS)
- Підтримка при верифікації на маркетплейсах
- 30 днів технічної підтримки після деплою
Строки
| Тип задачі |
Приблизний строк |
| Базовий ERC-721 без reveal |
від 2 тижнів |
| NFT-колекція з allowlist, reveal, VRF |
від 5 тижнів |
| ERC-1155 з marketplace та роялті |
від 6 тижнів |
| Dynamic NFT із зовнішніми даними |
від 8 тижнів |
Вартість розраховується індивідуально після аудиту вашого завдання. Надішліть brief з описом проекту — оцінимо прозоро протягом 3 робочих днів. Для постійних клієнтів діє гнучка система знижок на пакетні замовлення. Зв'яжіться з нами для детального обговорення вашого NFT-проекту. Отримайте консультацію з архітектури маркетплейсу — залиште заявку, і ми оцінимо проект за три дні.