Реализация DAO-портала на сайте
Децентрализованные автономные организации (DAO) сталкиваются с проблемами: газ-затратные транзакции отталкивают участников, отсутствие делегирования снижает влияние мелких держателей, а ручное создание предложений требует calldata — боль для пользователей. Мы построили портал, который закрывает эти пробелы: предложения с квадратичным взвешиванием, делегирование, интеграция off-chain Snapshot для сигнальных голосов и индексация через The Graph. Расскажу как это сделать правильно.
Как DAO-портал решает проблемы управления?
Первая проблема — вовлечённость. Если голосование требует gas, пользователи уходят. Snapshot решает это off-chain, но исполнение остаётся on-chain. Мы подключаем Timelock Controller: сигнальное голосование → исполнительский Governor. Вторая — централизация прав. Без делегирования мелкие держатели не влияют. Вводим Delegation Mapping (как Aave). Третья — сложность создания предложений. Ручное кодирование calldata — боль. Делаем форму с автогенерацией бинарных данных через encodeFunctionData из Viem.
Архитектура DAO-портала
Двухуровневая: off-chain (Snapshot) для сигнальных голосов и on-chain (Governor + Timelock) для обязательных. Governance Token использует EIP-2612 для делегирования без лишних транзакций. Размер одного on-chain голосования — около 200 000 gas, но с L2 (Arbitrum, Polygon) затраты падают до 500 gas.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin/contracts/governance/Governor.sol"; import "@openzeppelin/contracts/governance/extensions/GovernorSettings.sol"; import "@openzeppelin/contracts/governance/extensions/GovernorCountingSimple.sol"; import "@openzeppelin/contracts/governance/extensions/GovernorVotes.sol"; import "@openzeppelin/contracts/governance/extensions/GovernorTimelockControl.sol"; contract DAOGovernor is Governor, GovernorSettings, GovernorCountingSimple, GovernorVotes, GovernorTimelockControl { constructor( IVotes _token, TimelockController _timelock ) Governor("DAO Governor") GovernorSettings( 1 days, // voting delay 7 days, // voting period 100_000e18 // proposal threshold ) GovernorVotes(_token) GovernorTimelockControl(_timelock) {} // 4% токенов нужно для кворума function quorum(uint256 blockNumber) public view override returns (uint256) { return token.getPastTotalSupply(blockNumber) * 4 / 100; } } Frontend: создание предложения
Пользователь заполняет форму: адрес контракта, метод (ABI), аргументы. Кодируем вызов, отправляем транзакцию в Governor. Используем wagmi и Viem.
import { useWriteContract, useAccount } from 'wagmi'; import { encodeFunctionData } from 'viem'; function CreateProposal() { const { writeContractAsync } = useWriteContract(); const handleSubmit = async (formData: ProposalFormData) => { const calldata = encodeFunctionData({ abi: treasuryAbi, functionName: 'transfer', args: [formData.recipient, formData.amount] }); await writeContractAsync({ address: GOVERNOR_ADDRESS, abi: governorAbi, functionName: 'propose', args: [ [TREASURY_ADDRESS], [0n], [calldata], formData.description ] }); }; return <ProposalForm onSubmit={handleSubmit} />; } Frontend: голосование
Отображаем вес голоса на момент снепшота. Кнопки «За», «Против», «Воздержаться». Опционально — причина.
function VoteOnProposal({ proposalId }) { const { writeContractAsync } = useWriteContract(); const { address } = useAccount(); const { data: votingPower } = useReadContract({ address: GOVERNOR_ADDRESS, abi: governorAbi, functionName: 'getVotes', args: [address!, proposalSnapshotBlock] }); const castVote = async (support: 0 | 1 | 2) => { await writeContractAsync({ address: GOVERNOR_ADDRESS, abi: governorAbi, functionName: 'castVoteWithReason', args: [proposalId, support, reason] }); }; return ( <div> <p>Ваш вес голоса: {formatTokens(votingPower)} токенов</p> <button onClick={() => castVote(1)}>За</button> <button onClick={() => castVote(0)}>Против</button> <button onClick={() => castVote(2)}>Воздержаться</button> </div> ); } Индексирование через The Graph
Чтобы показывать историю предложений и результаты без постоянных запросов к ноде, используем The Graph. Поднимаем subgraph для Governor контракта.
const PROPOSALS_QUERY = gql` query GetProposals($state: String, $first: Int, $skip: Int) { proposals( where: { state: $state } orderBy: createdAt orderDirection: desc first: $first skip: $skip ) { id proposalId proposer { id } description state forVotes againstVotes abstainVotes quorum startBlock endBlock createdAt } } `; Интеграция Snapshot для сигнальных голосов
Для сигнальных голосований без газа — Snapshot. Подписываем мета-транзакцию, данные хранятся в IPFS.
import snapshot from '@snapshot-labs/snapshot.js'; const client = new snapshot.Client712('https://hub.snapshot.org'); await client.proposal(web3, address, { space: 'your-dao.eth', type: 'single-choice', title: 'Принять новую стратегию токенов?', body: '## Описание\n\nПолное описание предложения...', choices: ['За', 'Против', 'Воздержаться'], start: Math.floor(Date.now() / 1000), end: Math.floor(Date.now() / 1000) + 7 * 24 * 3600, snapshot: await web3.eth.getBlockNumber(), plugins: JSON.stringify({}), app: 'your-dao-app' }); Почему on-chain голосование с Timelock безопаснее?
Timelock Controller задерживает исполнение на заданное время (например, 2 дня). Это даёт возможность отменить вредоносное решение, если его заметить. Рекомендуем добавлять роль CANCELLER для мультисига. Такой подход используют ведущие DAO — Compound, Uniswap, Aave.
Процесс разработки DAO-портала
- Аналитика: определяем механики голосования, кворум, пороги, типы предложений. Выбираем стандарты токенов (ERC-20, ERC-721). Результат — спецификация контрактов.
- Проектирование: архитектура контрактов, схемы данных, frontend-маршруты. Результат — техническое задание.
- Разработка: пишем смарт-контракты (OpenZeppelin), frontend (Next.js + Wagmi/Viem), subgraph (The Graph). Результат — код с тестами.
- Аудит: статический анализ (Slither, MythX) и внешний аудит. Результат — отчёт аудита.
- Деплой: разворачиваем на выбранной сети (Ethereum, Polygon, Arbitrum), настраиваем IPFS, деплоим интерфейс на Vercel/Cloudflare. Результат — запуск в мейннете.
- Мониторинг: подключаем дашборд Tenderly для отслеживания транзакций. Результат — дашборд операций.
Сравнение: готовые платформы vs индивидуальная разработка
| Критерий | Готовые решения (Snapshot, Aragon) | Индивидуальная разработка |
|---|---|---|
| Кастомизация | Ограничена шаблонами | Полный контроль над контрактами и интерфейсом |
| Безопасность | Зависит от аудита платформы | Возможность усилить аудит под свои риски |
| Интеграции | Только встроенные | Любые внешние сервисы |
| Гибкость механик | Фиксированные типы голосования | Квадратичное, весовое, multichain |
| Расходы на газ | Нативная оптимизация не всегда | Оптимизируем calldata, используем L2 |
Индивидуальная разработка оправдана, когда нужна уникальная токеномика или строгие требования к безопасности. Она в 3 раза быстрее позволяет адаптировать продукт под специфические задачи сообщества.
Результат и сроки
Вы получаете полный набор смарт-контрактов (Governor, Timelock, Governance Token) с тестами и документацией. Frontend на Next.js с роутингом, формами, отображением данных. Интеграция с Snapshot (опционально). Индексер The Graph. Инструкция по развертыванию и обучение команды.
Мы гарантируем чистоту кода, следование Solidity best practices и использование проверенных паттернов OpenZeppelin. Каждый контракт проходит многоуровневый аудит. Более 50 успешных запусков DAO в мейннете — от DeFi до NFT-сообществ.
Типичные ошибки при разработке DAO: неправильная настройка quorum (слишком низкий или высокий), игнорирование отзыва голоса (revocation), отсутствие механизма обновления контрактов (upgradeability).
Сроки реализации: базовая реализация (контракты + full-stack) занимает от 4 до 8 недель. Стоимость рассчитывается индивидуально. Для оценки вашего проекта свяжитесь с нами — мы подготовим предложение в течение 2 дней.







