Доступ до членів DAO через NFT
Членство в NFT — альтернатива управлінню зважено на токени. Замість "1 токен = 1 голос" це "1 NFT = 1 голос" або "власність NFT = доступ." Це вирішує частину проблем плутократії: великий емітент не домінує автоматично. MolochDAO, Guild.xyz, Friends With Benefits — приклади систем, де членство визначається NFT або whitelist, не просто балансом токенів.
Технічно завдання розбивається на два: сам контракт членства NFT й система контролю доступу, яка перевіряє власність NFT для захищених операцій.
Контракт членства NFT
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
contract DAOMembershipNFT is ERC721, AccessControl {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
// Soulbound: NFT неможна передати
bool public isSoulbound;
// Merkle root для whitelist мінтингу
bytes32 public merkleRoot;
uint256 private _tokenIdCounter;
uint256 public maxSupply;
// Система рівнів: 1 = Член, 2 = Core, 3 = Засновник
mapping(uint256 => uint8) public memberTier;
mapping(address => bool) public hasMinted;
event MemberAdded(address indexed member, uint256 tokenId, uint8 tier);
event MemberRevoked(uint256 indexed tokenId);
constructor(
string memory name,
string memory symbol,
uint256 _maxSupply,
bool _soulbound
) ERC721(name, symbol) {
maxSupply = _maxSupply;
isSoulbound = _soulbound;
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
// Мінтинг через Merkle proof (для запуску whitelist)
function mintWithProof(
bytes32[] calldata proof,
uint8 tier
) external {
require(!hasMinted[msg.sender], "Already minted");
require(_tokenIdCounter < maxSupply, "Max supply reached");
bytes32 leaf = keccak256(abi.encodePacked(msg.sender, tier));
require(MerkleProof.verify(proof, merkleRoot, leaf), "Invalid proof");
hasMinted[msg.sender] = true;
_mintMember(msg.sender, tier);
}
// Мінтинг через рішення управління
function mintByGovernance(
address to,
uint8 tier
) external onlyRole(MINTER_ROLE) {
require(!hasMinted[to], "Already has membership");
_mintMember(to, tier);
}
function _mintMember(address to, uint8 tier) internal {
uint256 tokenId = ++_tokenIdCounter;
memberTier[tokenId] = tier;
_safeMint(to, tokenId);
emit MemberAdded(to, tokenId, tier);
}
// Soulbound: блокування передач
function _beforeTokenTransfer(
address from, address to, uint256 tokenId, uint256 batchSize
) internal override {
super._beforeTokenTransfer(from, to, tokenId, batchSize);
if (isSoulbound && from != address(0) && to != address(0)) {
revert("Soulbound: non-transferable");
}
}
// Відкликати членство через управління (burn)
function revoke(uint256 tokenId) external onlyRole(MINTER_ROLE) {
address owner = ownerOf(tokenId);
hasMinted[owner] = false;
_burn(tokenId);
emit MemberRevoked(tokenId);
}
}
Soulbound vs передаваємі NFT
Soulbound (ERC-5192) — NFT неможна продати чи передати. Членство пов'язане з особистістю, не капіталом. Підходить для DAO, де важлива справжня особистість учасника (контрибьютор DAO, професійні гільдії). Недолік: якщо ключ втрачено — немає способу передати членство без голосування управління.
Передаваємі членства NFT — працюють як коллекційні токени доступу (наприклад, Nouns DAO). Можна продати місце в DAO. Більш ліквідні; створює ринкову ціну для членства. Ризик: спекулятивний ринок може спотворити склад DAO.
Система рівнів
Простого "так/ні" недостатньо для складних DAO. Рівні гнучкості:
| Рівень | Назва | Права | Отримання |
|---|---|---|---|
| 1 | Observer | Читати закриті обговорення | Whitelist mint |
| 2 | Член | Голосування, пропозиції | Активність 30+ днів |
| 3 | Core | Комітет грантів, veto | Голосування спільноти |
| 4 | Засновник | Мультисиг казни | Тільки команда засновників |
Підвищення рівня через пропозицію управління: будь-який член Tier 2 може номінувати іншого для Tier 3. Governor голосує; якщо пройшла, MINTER_ROLE мінтує оновлення.
On-chain верифікація власності
Контроль доступу до функцій DAO через перевірку балансу:
contract DAOGovernanceWithNFT {
DAOMembershipNFT public membershipNFT;
modifier onlyMember() {
require(membershipNFT.balanceOf(msg.sender) > 0, "Not a member");
_;
}
modifier onlyTier(uint8 minTier) {
uint256 balance = membershipNFT.balanceOf(msg.sender);
require(balance > 0, "Not a member");
// Знайти найвищий рівень члена
uint8 highestTier = _getHighestTier(msg.sender);
require(highestTier >= minTier, "Insufficient tier");
_;
}
function createProposal(...) external onlyMember() { ... }
function accessTreasury(...) external onlyTier(3) { ... }
function _getHighestTier(address member) internal view returns (uint8) {
uint256 balance = membershipNFT.balanceOf(member);
uint8 highest = 0;
// Для малих DAO (< 1000 членів) можна ітерувати
for (uint256 i = 0; i < balance; i++) {
uint256 tokenId = membershipNFT.tokenOfOwnerByIndex(member, i);
uint8 tier = membershipNFT.memberTier(tokenId);
if (tier > highest) highest = tier;
}
return highest;
}
}
Для DAO з тисячами членів ітерація в on-chain функції — проблема. Альтернатива: зберігати address => uint8 tier маппінг у контракті, оновлений при mint/burn.
Off-chain верифікація через підпис
Для доступу до закритих каналів Discord, внутрішніх сайтів або off-chain ресурсів — верифікація через підпис гаманця без транзакції:
// Frontend: користувач підписує повідомлення
const message = `Verify DAO membership\nTimestamp: ${Date.now()}\nAddress: ${address}`;
const signature = await signer.signMessage(message);
// Backend: перевірити підпис + власність
async function verifyMembership(address: string, signature: string): Promise<boolean> {
// Відновити адресу з підпису
const recovered = ethers.verifyMessage(message, signature);
if (recovered.toLowerCase() !== address.toLowerCase()) return false;
// Перевірити баланс NFT через RPC
const nft = new ethers.Contract(NFT_ADDRESS, ABI, provider);
const balance = await nft.balanceOf(address);
return balance.gt(0);
}
Цей паттерн використовується Guild.xyz та Collab.Land для Discord гейтингу. Користувач не платить газ, просто підписує — й отримує роль Discord при наявності NFT.
Графік розробки
Дизайн (3-5 днів). Структура рівнів, soulbound vs передаваємий, механіка запуску (Merkle whitelist, публічний mint, тільки управління), інтеграція з існуючим Governor або новий.
Розробка контрактів (1,5-2 тижні). Членство NFT + інтеграція управління + тестування.
Off-chain інтеграція (1 тиждень). Верифікація бекенду, бот Discord/Telegram через Collab.Land або користувацький.
Аудит (1 тиждень). Контракти NFT з контролем доступу вимагають аудиту, особливо логіки відкликання.
Графік і вартість — після деталізації вимог.







