Merkle Tree whitelist для NFT — розробка під ключ

Колекція з 10 000 NFT з whitelist на 3 000 адрес. Зберігати whitelist в on-chain mapping — це 3 000 операцій SSTORE при заповненні, близько 0.5–0.8 ETH газу (≈$1,500–$2,500 USD за поточним курсом). Merkle Tree вирішує проблему: одна транзакція для кореня, 3–5k gas на верифікацію кожного мінта. Реаль

Напрямки блокчейн-розробки

Часті запитання

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1441
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1301
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    998
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1267
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    713
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    1003

Колекція з 10 000 NFT з whitelist на 3 000 адрес. Зберігати whitelist в on-chain mapping — це 3 000 операцій SSTORE при заповненні, близько 0.5–0.8 ETH газу (≈$1,500–$2,500 USD за поточним курсом). Merkle Tree вирішує проблему: одна транзакція для кореня, 3–5k gas на верифікацію кожного мінта. Реальна економія — в 10+ разів порівняно з mapping. Правильна реалізація із захистом від double-leaf атаки та підтримкою multi-tier — завдання, яке ми вирішуємо вже понад 5 років для 30+ проєктів. OpenZeppelin MerkleProof забезпечує стандартну верифікацію. Замовте розробку під ключ — отримайте смарт-контракт, генератор proof-ів та frontend.

Побудова дерева Меркла для списку дозволених адрес

Листя дерева — keccak256 хеші адрес (іноді з додатковими даними: keccak256(abi.encodePacked(address, maxMintAmount))). Хеші сусідніх листів об'єднуються і хешуються від низу до верху. Корінь (root) — один bytes32, що зберігається в контракті. Алгоритм:

import { MerkleTree } from 'merkletreejs' import { keccak256, encodePacked } from 'viem' const leaves = whitelist.map(addr => keccak256(encodePacked(['address'], [addr])) ) const tree = new MerkleTree(leaves, keccak256, { sortPairs: true }) const root = tree.getHexRoot() // → bytes32 для контракту 

sortPairs: true — критичний параметр. Він забезпечує детерміновану побудову дерева незалежно від порядку листів. Без нього один і той же список дає різні root-и при різному порядку адрес.

Верифікація в контракті

import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; bytes32 public merkleRoot; function mint(uint256 amount, bytes32[] calldata proof) external payable { bytes32 leaf = keccak256(abi.encodePacked(msg.sender)); require(MerkleProof.verify(proof, merkleRoot, leaf), "Invalid proof"); // ... mint logic } 

MerkleProof.verify() з OpenZeppelin — стандартна реалізація з O(log n) складністю. Для 3 000 адрес — proof з 12 хешів (log2(3000) ≈ 12). Для 100 000 адрес — 17 хешів. Газові витрати верифікації зростають повільно.

Захист від double-leaf атаки та double-mint

Класична проблема Merkle Tree в смарт-контрактах: якщо leaf не унікальний — один proof може верифікувати декілька листів. Захист в OpenZeppelin MerkleProof 4.7+: бібліотека перевіряє, що leaf != internal_node. Додатковий захист: хешуйте лист двічі keccak256(keccak256(abi.encodePacked(addr))). Це робить співпадіння практично неможливим.

Розширений whitelist: тири та кількості

Простий список — тільки перевірка «адреса є / немає». Для багаторівневих whitelist (tier 1: 2 NFT, tier 2: 1 NFT) — включаємо дані в leaf:

bytes32 leaf = keccak256(abi.encodePacked(msg.sender, maxAmount)); require(MerkleProof.verify(proof, merkleRoot, leaf), "Invalid proof"); require(amount <= maxAmount, "Exceeds allocation"); 

Тепер proof верифікує не тільки адресу, але й максимальну кількість для цієї адреси. Одне дерево, один root, різні алокації.

Захист від повторного мінту

Merkle Proof тільки верифікує право мінтити — не запобігає повторному мінту. Потрібен окремий mapping використаних алокацій:

mapping(address => uint256) public mintedAmount; function mint(uint256 amount, uint256 maxAmount, bytes32[] calldata proof) external { require(mintedAmount[msg.sender] + amount <= maxAmount, "Exceeds allocation"); mintedAmount[msg.sender] += amount; // ... } 

mintedAmount займає SSTORE тільки при першому мінті користувача — це O(unique_minters) storage, а не O(whitelist_size).

Off-chain дистрибуція proof-ів

Три підходи до того, як користувач отримує свій proof.

Статичний JSON в IPFS/CDNПросто і без бекенду: { "0xABC...": ["0x...", "0x..."] } генерується один раз, публікується в CDN.
Backend API дає гнучкість для додавання адрес. On-chain events через The Graph — децентралізація. Для більшості проєктів підходить JSON в CDN.

Метод Складність Гнучкість Вартість
Статичний JSON (IPFS/CDN) Низька Низька Мінімальна
Backend API Середня Висока Середня
The Graph Висока Середня Середня

Оновлення списку після деплою

Якщо контракт дозволяє зміну merkleRoot (через onlyOwner), то список можна оновити без передеплою. Будуємо нове дерево з доповненнями, оновлюємо root через setMerkleRoot(). Після зміни root старі proof-и перестають працювати. Необхідно повідомити користувачів.

Переваги Merkle Tree перед mapping

Порівняння газу для типового проєкту з 3000 адрес:

Етап Mapping (3000 адрес) Merkle Tree (3000 адрес)
Setup 3000 SSTORE (≈0.5-0.8 ETH ≈ $1,500-$2,500 USD) 1 SSTORE (≈0.0001 ETH ≈ $0.30 USD)
Mint (верифікація) 1 SLOAD + 1 SSTORE (≈5000 gas) 12 хешів + 1 SSTORE (≈3000 gas)
Оновлення списку Перезапис всіх адрес (дорого) 1 транзакція (≈0.0001 ETH)

Merkle Tree кращий ніж mapping у 10+ разів при setup та в 1.5–2 рази при кожному mint. Для колекції на 10 000 токенів зі списком на 3 000 адрес економія газу на етапі заповнення становить 0.5–0.8 ETH.

Що входить в розробку whitelist під ключ

  1. Аналіз вимог: тири, ліміти, стандарт ERC-721.
  2. Розробка смарт-контракту з Merkle proof верифікацією (Solidity з OpenZeppelin).
  3. Генерація дерева Меркла та proof-ів (Node.js, бібліотека merkletreejs).
  4. Створення API або статичного JSON для дистрибуції proof-ів.
  5. Frontend інтеграція (wagmi, RainbowKit).
  6. Тестування (Foundry: unit-тести, перевірка коректних/некоректних proof, double-mint захист).
  7. Документація.

Орієнтовні терміни та вартість

Базова реалізація — 2–3 дні. З тирами, API та frontend — до 5 днів. Вартість розраховується індивідуально після аналізу вашого проєкту. Зв'яжіться з нами для точної оцінки — ми підготуємо комерційну пропозицію.

Чому варто довірити розробку нам?

Ми гарантуємо безпеку та якість. Наш досвід у блокчейн-розробці (Ethereum, Polygon, Arbitrum, Solana) налічує понад 5 років. Ми реалізували більше 30 смарт-контрактів, включаючи NFT колекції з whitelist. Кожна реалізація проходить внутрішній code review та формальну верифікацію ключових функцій. Отримайте консультацію — наші інженери готові проаналізувати ваш проєкт і запропонувати оптимальне рішення.