Колекція з 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.
| Метод | Складність | Гнучкість | Вартість |
|---|---|---|---|
| Статичний 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 під ключ
- Аналіз вимог: тири, ліміти, стандарт ERC-721.
- Розробка смарт-контракту з Merkle proof верифікацією (Solidity з OpenZeppelin).
- Генерація дерева Меркла та proof-ів (Node.js, бібліотека merkletreejs).
- Створення API або статичного JSON для дистрибуції proof-ів.
- Frontend інтеграція (wagmi, RainbowKit).
- Тестування (Foundry: unit-тести, перевірка коректних/некоректних proof, double-mint захист).
- Документація.
Орієнтовні терміни та вартість
Базова реалізація — 2–3 дні. З тирами, API та frontend — до 5 днів. Вартість розраховується індивідуально після аналізу вашого проєкту. Зв'яжіться з нами для точної оцінки — ми підготуємо комерційну пропозицію.
Чому варто довірити розробку нам?
Ми гарантуємо безпеку та якість. Наш досвід у блокчейн-розробці (Ethereum, Polygon, Arbitrum, Solana) налічує понад 5 років. Ми реалізували більше 30 смарт-контрактів, включаючи NFT колекції з whitelist. Кожна реалізація проходить внутрішній code review та формальну верифікацію ключових функцій. Отримайте консультацію — наші інженери готові проаналізувати ваш проєкт і запропонувати оптимальне рішення.







