У нашій практиці ми стикалися з десятками інтеграцій NFT-membership і бачили, як помилкова архітектура контракту призводить до витоку привілеїв. Найпоширеніша помилка: розробники реалізують перевірку ownerOf(tokenId) == msg.sender і вважають задачу вирішеною. Але NFT можна позичити, flash loan-нути (на один блок) або виставити на маркетплейс, зберігши доступ через делегування. Правильна membership система потребує розуміння цих векторів і явного вибору моделі довіри. Наш досвід — 5 років у DeFi та NFT, більше 20 успішних проєктів — гарантує надійне рішення.
Контрактна архітектура
Базова модель: володіння токеном
Для простих кейсів (доступ до контенту, Discord-верифікація) достатньо ERC-721 з функцією перевірки balanceOf(user) > 0. balanceOf дешевший за ownerOf при множинних токенах і стійкіший до edge cases. Але вона не захищає від listing: власник може виставити NFT на OpenSea, отримати доступ до закритого контенту, а потім зняти лістинг.
Тиєрна membership через ERC-1155
Для кількох рівнів доступу (Bronze/Silver/Gold, або місяць/рік/lifetime) ERC-1155 нативно підходить краще, ніж ERC-721. Кожен tokenId — тир:
uint256 public constant TIER_BRONZE = 1; uint256 public constant TIER_SILVER = 2; uint256 public constant TIER_GOLD = 3; function getMemberTier(address user) external view returns (uint256) { if (balanceOf(user, TIER_GOLD) > 0) return TIER_GOLD; if (balanceOf(user, TIER_SILVER) > 0) return TIER_SILVER; if (balanceOf(user, TIER_BRONZE) > 0) return TIER_BRONZE; return 0; // не учасник } Тири з накопиченим доступом: Gold включає все, що є в Silver та Bronze. Перевіряємо зверху вниз.
Чому варто використовувати soulbound токени для membership?
Soulbound токени (EIP-5192) позбавлені проблеми переносимості: вони прив'язані до адреси власника назавжди. Якщо мета — прив'язати доступ до конкретної людини, а не гаманця, використовуємо EIP-5192 або просто перевизначаємо transfer функції:
function _beforeTokenTransfer( address from, address to, uint256 tokenId, uint256 batchSize ) internal override { require(from == address(0) || to == address(0), "Soulbound: non-transferable"); super._beforeTokenTransfer(from, to, tokenId, batchSize); } from == address(0) — mint, to == address(0) — burn. Все інше заборонено. Проблема: втрата ключів = втрата membership. Рішення: передбачити recovery механізм через мультисиг або соціальне відновлення (ERC-4337 account abstraction).
Як реалізувати тимчасові підписки в ERC-721?
Expiring membership потребує зберігання дат. Два підходи:
| Підхід | On-chain timestamp | Signature-based off-chain |
|---|---|---|
| Зберігання | Маппінг tokenId → expiresAt | Підписані JWT з expiry (на бекенді) |
| Газ на перевірку | Запис у сховище | Без записів, дешевше |
| Довіра | Повне (все on-chain) | Потрібна довіра до сервісу підписів |
| Підходить для | Fully on-chain систем | Web2-hybrid та інтеграцій з API |
Для fully on-chain — перший підхід. ERC-5643 — чернетка стандарту для subscription NFT з renewSubscription(uint256 tokenId, uint64 duration).
Порівняння моделей membership
| Характеристика | ERC-721 | ERC-1155 | Soulbound (EIP-5192) |
|---|---|---|---|
| Тири | 1 тип | Множина тирів | 1 тип (якщо один) |
| Передача | Так | Так | Ні |
| Gas на перевірку | balanceOf ~30k |
balanceOfBatch ~40k |
balanceOf ~30k |
| Складність | Низька | Середня | Середня |
| Застосування | Прості доступи | Tiered membership | Персональні підписки |
Інтеграція з off-chain системами
Верифікація через EIP-1271
Для перевірки membership у бекенді без транзакцій: користувач підписує повідомлення (EIP-191 або EIP-712), бекенд верифікує через eth_call до isValidSignature(bytes32 hash, bytes signature) для смарт-гаманців або через ecrecover для EOA. Це стандартний смарт-контракт членства поза ланцюгом.
Делегування через delegate.xyz
delegate.cash (де-факто стандарт) дозволяє власнику NFT делегувати cold wallet → hot wallet. Для membership систем це важливо: держателі зберігають дорогий NFT у cold wallet, а взаємодіють через гарячий. Інтеграція:
IDelegationRegistry constant DELEGATION_REGISTRY = IDelegationRegistry(0x00000000000076A84feF008CDAbe6409d2FE638B); function isMember(address user) public view returns (bool) { if (balanceOf(user) > 0) return true; // Перевіряємо делегування address[] memory delegators = DELEGATION_REGISTRY.getDelegationsByDelegate(user); for (uint i = 0; i < delegators.length; i++) { if (balanceOf(delegators[i]) > 0) return true; } return false; } Це реальна потреба: Moonbirds, Doodles та інші великі колекції інтегрували delegate.cash саме для цього.
Mint механізм та ціноутворення
Allowlist через Merkle Tree — стандарт для presale. Економія газу сягає 70% порівняно зі зберіганням списку на контракті:
bytes32 public merkleRoot; function allowlistMint(bytes32[] calldata proof) external payable { bytes32 leaf = keccak256(abi.encodePacked(msg.sender)); require(MerkleProof.verify(proof, merkleRoot, leaf), "Invalid proof"); require(msg.value >= PRICE, "Insufficient payment"); _safeMint(msg.sender, _nextTokenId()); } Proof генерується off-chain (merkletreejs), root завантажується в контракт. Список на 10,000 адрес — proof з ~14 хешів, calldata ~450 байт.
Приклад розрахунку Merkle proof для 10,000 адрес
Дерево глибини 14 містить 16384 листа. Для proof з 14 хешів (32 байти кожен) calldata складає 14*32 + 4 (зміщення) ≈ 452 байти. При gas price 100 gwei економія відносно зберігання списку на контракті — близько 0.005 ETH.
Що входить в роботу
- Аудит поточної архітектури та постановка вимог
- Розробка смарт-контрактів (Solidity 0.8.x, OpenZeppelin)
- Написання unit-тестів (Foundry/Hardhat) з покриттям edge cases
- Інтеграція з бекендом (EIP-1271, delegate.cash) та фронтендом (wagmi, RainbowKit)
- Документація контрактів (NatSpec) та інструкції з розгортання
- Розгортання в mainnet/testnet, налаштування верифікації в Etherscan
- Підтримка після запуску (опціонально)
Орієнтири за термінами
ERC-721 membership з тирами та Merkle allowlist — від 2 днів. Додавання тимчасової підписки (ERC-5643 стиль) + бекенд верифікація — ще 1-2 дні. Повна система з делегуванням, soulbound recovery та фронтендом — 4-5 днів. Зв'яжіться з нами для точної оцінки вашого проєкту. Замовте розробку системи та отримайте консультацію нашого інженера.







