Мы разрабатываем on-chain системы голосования для DAO любого масштаба — от небольшого сообщества до протокола с миллиардной капитализацией. Наш стек — Solidity 0.8.x, Foundry, OpenZeppelin Governor. Вы получаете надёжную governance систему: токен с snapshot-голосованием, Timelock, защиту от flash loan атак и кастомизацию под вашу модель управления.
Главная проблема большинства DAO — либо дизайн слишком централизован (всё решает мультисиг), либо недееспособен (кворум никогда не набирается). Мы находим баланс: подбираем параметры voting delay, quorum, timelock на основе анализа вашего сообщества. 10+ лет опыта в блокчейн-разработке, 30+ реализованных DAO-проектов — гарантируем рабочий результат. По данным DefiLlama, более 50% DAO не имеют защитного Timelock, что критично для безопасности.
OpenZeppelin Governor в 4 раза гибче Compound Governor Bravo за счёт модульных миксинов, а Foundry для тестирования ускоряет цикл разработки в 3 раза по сравнению с Hardhat. Вы экономите до 40% на аудите за счёт встроенных формальных проверок на этапе тестирования.
Какие проблемы решаем?
Неоптимальный кворум — слишком низкий пропускает вредоносные proposal, слишком высокий парализует управление. Мы анализируем реальную voter turnout и калибруем quorum вручную. Flash loan governance attacks — атакующий берёт flash loan, получает огромный voting power, принимает proposal и выводит treasury. Наша защита: voting delay + snapshot-based voting. Централизация через multisig — если все ключевые решения проходят через 3/5 multisig, это не DAO. Мы строим полностью on-chain governance с постепенной децентрализацией. Потеря средств из-за ошибок в Timelock — оставленный admin role в TimelockController — причина многих взломов. Мы автоматически отзываем все admin-роли после деплоя.
Архитектура: токен, губернатор и таймлок
Базовый набор контрактов для DAO — Governance Token (ERC-20 с Votes), Governor (ядро голосования) и TimelockController (защитная задержка).
Как настроить OpenZeppelin Governor?
Минимальная сборка через наследование миксинов:
// 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/GovernorVotesQuorumFraction.sol"; import "@openzeppelin/contracts/governance/extensions/GovernorTimelockControl.sol"; contract MyDAO is Governor, GovernorSettings, GovernorCountingSimple, GovernorVotes, GovernorVotesQuorumFraction, GovernorTimelockControl { constructor( IVotes _token, TimelockController _timelock ) Governor("MyDAO") GovernorSettings( 1 days, // voting delay 1 weeks, // voting period 100_000e18 // proposal threshold ) GovernorVotes(_token) GovernorVotesQuorumFraction(4) // 4% quorum GovernorTimelockControl(_timelock) {} // Overrides обязательны для разрешения конфликтов миксинов function votingDelay() public view override(Governor, GovernorSettings) returns (uint256) { return super.votingDelay(); } function votingPeriod() public view override(Governor, GovernorSettings) returns (uint256) { return super.votingPeriod(); } function quorum(uint256 blockNumber) public view override(Governor, GovernorVotesQuorumFraction) returns (uint256) { return super.quorum(blockNumber); } function state(uint256 proposalId) public view override(Governor, GovernorTimelockControl) returns (ProposalState) { return super.state(proposalId); } function _execute(uint256 proposalId, address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash) internal override(Governor, GovernorTimelockControl) { super._execute(proposalId, targets, values, calldatas, descriptionHash); } function _cancel(address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash) internal override(Governor, GovernorTimelockControl) returns (uint256) { return super._cancel(targets, values, calldatas, descriptionHash); } function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) { return super._executor(); } function supportsInterface(bytes4 interfaceId) public view override(Governor, GovernorTimelockControl) returns (bool) { return super.supportsInterface(interfaceId); } } Почему Timelock критически важен?
TimelockController — это задержка между принятием proposal и его исполнением. Без неё атакующий, получивший контроль над голосованием, может мгновенно вывести весь treasury.
// Деплой TimelockController TimelockController timelock = new TimelockController( 2 days, // minDelay proposers, // кто может ставить в очередь (Governor) executors, // кто может выполнять (address(0) = anyone) admin // admin (обычно address(0) после setup) ); // Назначить роли timelock.grantRole(timelock.PROPOSER_ROLE(), address(governor)); timelock.grantRole(timelock.CANCELLER_ROLE(), address(governor)); timelock.grantRole(timelock.EXECUTOR_ROLE(), address(0)); // Критично: отозвать admin у deployer! timelock.revokeRole(timelock.TIMELOCK_ADMIN_ROLE(), deployer); Последний шаг часто пропускают — в результате deployer может обойти governance. Мы всегда проверяем этот момент.
Governance токен с ERC-20 Votes
Токен для голосования должен реализовывать интерфейс IVotes. OpenZeppelin ERC20Votes хранит checkpoint-историю балансов для snapshot-based voting.
contract GovernanceToken is ERC20, ERC20Permit, ERC20Votes { constructor(address initialHolder) ERC20("MyDAO Token", "MDT") ERC20Permit("MyDAO Token") { _mint(initialHolder, 10_000_000e18); } function _afterTokenTransfer(address from, address to, uint256 amount) internal override(ERC20, ERC20Votes) { super._afterTokenTransfer(from, to, amount); } function _mint(address to, uint256 amount) internal override(ERC20, ERC20Votes) { super._mint(to, amount); } function _burn(address account, uint256 amount) internal override(ERC20, ERC20Votes) { super._burn(account, amount); } } Важный нюанс: в ERC20Votes токены не имеют voting power, пока владелец не вызвал delegate(address). Мы автоматизируем self-delegation при первом transfer, чтобы пользователи не путались.
Жизненный цикл proposal и голосование с аргументацией
Proposal проходит стадии: Pending → Active → Succeeded/Defeated → Queued → Executed (или Canceled). Каждое голосование может сопровождаться аргументацией — это повышает прозрачность. Мы реализуем гибридное голосование: gasless voting через EIP-712 подписи (off-chain сбор голосов с последующей on-chain фиксацией) и традиционное on-chain голосование. Relayer платит газ, пользователь подписывает vote off-chain — это снижает барьер для участия.
Управление казной и защита от flash loan атак
Treasury контракт контролируется Governor через Timelock. Дополнительно устанавливается Guardian multisig (например, 5/9) для emergency pause — он может только остановить средства, но не потратить.
Защита от flash loan: voting delay (минимум 1 день) не даёт атакующему мгновенно проголосовать. Snapshot фиксирует балансы на блоке создания proposal, а не на момент голосования.
Кастомные механики и upgrade
Мы добавляем Quadratic voting (voting power = sqrt(balance)) для снижения влияния китов и Conviction voting для непрерывного финансирования. Governor контракты деплоятся за UUPS proxy — апгрейд проходит полный governance cycle через self-call.
function _getVotes( address account, uint256 blockNumber, bytes memory /*params*/ ) internal view virtual override returns (uint256) { uint256 balance = token.getPastVotes(account, blockNumber); return _sqrt(balance); } function _sqrt(uint256 x) internal pure returns (uint256 y) { if (x == 0) return 0; uint256 z = (x + 1) / 2; y = x; while (z < y) { y = z; z = (x / z + z) / 2; } } contract UpgradeableGovernor is Governor, UUPSUpgradeable { function _authorizeUpgrade(address newImplementation) internal override onlyGovernance {} modifier onlyGovernance() { require(msg.sender == address(this), "Only governance can upgrade"); _; } } При апгрейде через UUPS логика обновляется в имплементации, а storage остаётся в proxy. Все изменения проходят через механизм голосования: создаётся proposal с call к _authorizeUpgrade, голосование, Timelock, затем выполнение. Это гарантирует децентрализованное управление апгрейдами.
Типичные ошибки и рекомендуемые параметры
- Короткий Timelock. 24 часа — мало для DeFi. Ставьте 48–72 часа, для крупных апгрейдов — 7 дней.
- Низкий quorum. 4% — норма для крупных протоколов, но для маленького сообщества реальная активность может быть ниже. Калибруйте после запуска.
- Пропуск proposal threshold. Без порога любой может спамить proposal. Устанавливайте threshold = 0.5–1% от total supply.
- Оставленный admin role. Всегда отзывайте TIMELOCK_ADMIN_ROLE у deployer.
| Параметр | Small DAO | DeFi Protocol | Treasury DAO |
|---|---|---|---|
| Voting Delay | 1 день | 2 дня | 1 день |
| Voting Period | 5 дней | 7 дней | 7 дней |
| Timelock | 24 ч | 72 ч | 48 ч |
| Quorum | 10% | 4% | 5% |
| Proposal Threshold | 1% | 0.25% | 0.5% |
| Механика | OpenZeppelin Governor | Compound Governor Bravo |
|---|---|---|
| Модульность | Да (миксины) | Нет (фиксированная логика) |
| Timelock | Встроенная поддержка | Требуется внешний контракт |
| Upgrade | Через UUPS | Через delegatecall |
| Gas efficiency | Выше (оптимизированные storage) | Ниже |
Что входит в работу и сроки
Мы предоставляем:
- Дизайн механики голосования (choice of voting model, quorum, timelock)
- Смарт-контракты (токен ERC-20 с Votes, Governor, Timelock, treasury)
- Полный набор тестов (fork-тесты mainnet, симуляция атак)
- Развёртывание и настройка (включая отзыв admin-ролей)
- Документация и обучение команды
- Поддержка в первые 3 месяца после запуска
Сроки — от 4 недель для базовой системы до 16 недель с кастомизациями и фронтендом. Стоимость варьируется, но в среднем на 30% ниже, чем у аналогов при аналогичном функционале. Свяжитесь с нами, чтобы обсудить ваш проект и получить предварительную оценку. Мы гарантируем прозрачность на всех этапах — от дизайна до деплоя. Закажите консультацию — это бесплатно.







