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

Коллекция из 10 000 NFT с whitelist на 3 000 адресов. Хранить whitelist в on-chain mapping — это 3 000 операций SSTORE при заполнении, около 0.5–0.8 ETH газа. Merkle Tree решает проблему: одна транзакция для корня, 3–5k gas на верификацию каждого минта. Реальная экономия — в 10+ раз. Правильная реал

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

Часто задаваемые вопросы

Последние работы

  • 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 газа. Merkle Tree решает проблему: одна транзакция для корня, 3–5k gas на верификацию каждого минта. Реальная экономия — в 10+ раз. Правильная реализация с защитой от double-leaf атаки и поддержкой multi-tier — задача, которую мы решаем уже более 5 лет для 30+ проектов. Закажите разработку под ключ — получите смарт-контракт, генератор proof-ов и frontend.

Как построить Merkle Tree для whitelist?

Листья дерева — 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 — критичный параметр. Он обеспечивает детерминированное построение дерева независимо от порядка листьев. Без него один и тот же whitelist даёт разные 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 хэшей. Gas cost верификации растёт медленно.

Как защититься от double-leaf атаки и double-mint?

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

Расширенный whitelist: тиеры и количества

Простой 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, разные аллокации.

Защита от double-mint

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 (subgraph) Высокая Средняя Средняя

Обновление whitelist после деплоя

Если контракт позволяет смену merkleRoot (через onlyOwner), то whitelist можно обновить без передеплоя. Сценарий: основной whitelist + last-minute добавления. Строим новое дерево с дополнениями, обновляем root через setMerkleRoot(). Важно: после смены root старые proof-ы перестают работать. Пользователи с закешированными proof-ами получат revert. Нужна коммуникация об обновлении.

Почему Merkle Tree выгоднее mapping?

Сравнение газа для типового проекта с 3000 адресов:

Этап Mapping (3000 адресов) Merkle Tree (3000 адресов)
Setup 3000 SSTORE (≈0.5-0.8 ETH) 1 SSTORE (≈0.0001 ETH)
Mint (1 верификация) 1 SLOAD + 1 SSTORE (≈5000 gas) 12 хэшей + 1 SSTORE (≈3000 gas)
Обновление whitelist Перезапись всех адресов (дорого) 1 транзакция (≈0.0001 ETH)

Merkle Tree в 10+ раз эффективнее на setup и в 1.5–2 раза на каждом mint. Для коллекции на 10 000 токенов с whitelist в 3000 адресов экономия газа на этапе заполнения составляет 0.5–0.8 ETH.

Что входит в разработку whitelist под ключ

Процесс работы включает следующие этапы:

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

Ориентировочные сроки и стоимость

Базовая Merkle whitelist реализация — 2-3 дня. С тиерами, API для proof-ов и frontend интеграцией — до 5 дней. Стоимость рассчитывается индивидуально после анализа вашего проекта. Свяжитесь с нами для точной оценки — мы подготовим коммерческое предложение с учётом всех требований.

Почему стоит доверить разработку нам?

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