Реализация 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 дней.







