Разработка казначейства DAO
Treasury DAO — это не просто multisig кошелёк с деньгами. Это система управления активами, которая должна пережить смену команды, выдержать медвежий рынок и оставаться устойчивой к governance атакам. MakerDAO потеряла 35% стоимости treasury за 2022 год не из-за хаков — из-за неправильной аллокации в нативный токен. Правильная архитектура treasury начинается с диверсификации и заканчивается автоматизацией исполнения.
Архитектура treasury системы
Современное DAO treasury строится на нескольких уровнях с разными уровнями доступа:
Уровень 1: Core Treasury (Gnosis Safe + Governor)
Основное хранилище активов управляется через on-chain governance. Любая трата требует пройти полный governance цикл: proposal → voting → timelock → execution. TimelockController — обязательный посредник.
Governance Governor ──propose──► TimelockController ──execute──► Gnosis Safe
▲ (48h delay) │
│ ▼
Token holders Treasury assets
Gnosis Safe здесь — последнее хранилище, а не управляющий инструмент. Это важно: Safe не должен быть signers-managed для основных средств.
Уровень 2: Operational Budget (Sub-DAO Safe)
Отдельный Gnosis Safe для операционных расходов с лимитом (например, $50k/месяц). Управляется core team с 3/5 multisig. Пополняется из Core Treasury через регулярный governance proposal раз в квартал.
Уровень 3: Streaming Payments (Superfluid / Sablier)
Зарплаты и гранты через token streaming — контрибьюторы получают непрерывный поток токенов, который можно остановить в любой момент. Не нужно делать ручные выплаты.
Смарт-контракты treasury
Treasury Controller
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/governance/TimelockController.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
contract DAOTreasury is AccessControl {
using SafeERC20 for IERC20;
bytes32 public constant GOVERNOR_ROLE = keccak256("GOVERNOR_ROLE");
bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
// Лимиты для оперативных расходов без governance
mapping(address => uint256) public monthlySpendLimit;
mapping(address => mapping(uint256 => uint256)) public monthlySpent; // token -> month -> amount
event FundsDisbursed(address indexed token, address indexed recipient, uint256 amount, string reason);
event AllocationUpdated(address indexed token, uint256 amount, string strategy);
constructor(address _governor, address _operator) {
_grantRole(DEFAULT_ADMIN_ROLE, _governor);
_grantRole(GOVERNOR_ROLE, _governor);
_grantRole(OPERATOR_ROLE, _operator);
}
// Выплата через governance (без лимитов)
function disburse(
address token,
address recipient,
uint256 amount,
string calldata reason
) external onlyRole(GOVERNOR_ROLE) {
IERC20(token).safeTransfer(recipient, amount);
emit FundsDisbursed(token, recipient, amount, reason);
}
// Оперативные расходы с лимитом по месяцам
function operationalDisburse(
address token,
address recipient,
uint256 amount
) external onlyRole(OPERATOR_ROLE) {
uint256 currentMonth = block.timestamp / 30 days;
uint256 spent = monthlySpent[token][currentMonth];
require(
spent + amount <= monthlySpendLimit[token],
"Monthly limit exceeded"
);
monthlySpent[token][currentMonth] = spent + amount;
IERC20(token).safeTransfer(recipient, amount);
emit FundsDisbursed(token, recipient, amount, "operational");
}
function setMonthlyLimit(address token, uint256 limit)
external
onlyRole(GOVERNOR_ROLE)
{
monthlySpendLimit[token] = limit;
}
// ETH receive
receive() external payable {}
}
Budget Streams через Sablier V2
Sablier V2 LockupLinear — стандарт для streaming payments в DAO:
import { ISablierV2LockupLinear } from "@sablier/v2-core/interfaces/ISablierV2LockupLinear.sol";
import { LockupLinear, Broker } from "@sablier/v2-core/types/DataTypes.sol";
contract TreasuryStreaming {
ISablierV2LockupLinear public immutable sablier;
IERC20 public immutable daoToken;
// Создание стрима зарплаты контрибьютору
function createContributorStream(
address contributor,
uint128 totalAmount,
uint40 startTime,
uint40 endTime,
uint40 cliffDuration,
bool cancelable
) external returns (uint256 streamId) {
daoToken.approve(address(sablier), totalAmount);
LockupLinear.CreateWithDurations memory params = LockupLinear.CreateWithDurations({
sender: address(this),
recipient: contributor,
totalAmount: totalAmount,
asset: daoToken,
cancelable: cancelable, // true для испытательного срока
transferable: false, // нельзя передать права
durations: LockupLinear.Durations({
cliff: cliffDuration, // например, 3 месяца
total: endTime - startTime
}),
broker: Broker(address(0), ud60x18(0))
});
streamId = sablier.createWithDurations(params);
}
// Отмена стрима (при увольнении контрибьютора)
function cancelStream(uint256 streamId) external {
sablier.cancel(streamId);
// Не выплаченные средства автоматически возвращаются в treasury
}
}
Диверсификация активов
Хранить 80%+ treasury в нативном токене — стандартная ошибка молодых DAO. При медвежьем рынке такая treasury теряет 80-95% покупательной способности, и DAO не может финансировать разработку.
Рекомендуемая структура для протокола с $5M+ treasury
| Актив | Доля | Обоснование |
|---|---|---|
| Stablecoins (USDC, DAI) | 40-50% | Операционные расходы, runway |
| ETH | 20-30% | Ликвидный резерв, yield через staking |
| Нативный токен | 20-30% | Governance, incentives |
| Diversified DeFi (wBTC, etc.) | 0-10% | Опционально |
Yield на stablecoin часть
Idle stablecoins в treasury — упущенный yield. Популярные стратегии:
Aave/Compound: простое lending, 3-8% APY на USDC. Минимальный риск, instant withdrawal.
Maker DSR (DAI Savings Rate): официальный yield для DAI от MakerDAO.
Yearn Finance: автоматизированное управление yield стратегиями. Требует доверия к yearn vaults.
Governance proposal на каждое изменение yield стратегии — правильный подход для прозрачности.
Мониторинг и аналитика
On-chain метрики treasury
// Скрипт для ежедневного снапшота treasury
interface TreasurySnapshot {
timestamp: number;
assets: {
token: string;
balance: bigint;
usdValue: number;
}[];
totalUsdValue: number;
runwayMonths: number; // при текущем burn rate
}
async function getTreasurySnapshot(
provider: ethers.Provider,
treasuryAddress: string,
tokenList: string[]
): Promise<TreasurySnapshot> {
const assets = await Promise.all(
tokenList.map(async (token) => {
const contract = new ethers.Contract(token, ERC20_ABI, provider);
const balance = await contract.balanceOf(treasuryAddress);
const price = await getTokenPrice(token); // CoinGecko API
return {
token,
balance,
usdValue: Number(ethers.formatEther(balance)) * price
};
})
);
const totalUsdValue = assets.reduce((sum, a) => sum + a.usdValue, 0);
const monthlyBurn = await getMonthlyBurnRate(); // из исторических транзакций
return {
timestamp: Date.now(),
assets,
totalUsdValue,
runwayMonths: totalUsdValue / monthlyBurn
};
}
KPI дашборда
| Метрика | Цель | Алерт |
|---|---|---|
| Runway | > 24 месяца | < 12 месяцев |
| Stable ratio | > 40% | < 25% |
| Monthly burn | Известен и согласован | +20% превышение |
| Yield APY | > 4% на стейблы | < 2% |
| Token concentration | < 40% в native token | > 60% |
Защита от governance атак на treasury
Proposal threshold: создать proposal на вывод средств должно требовать значительного stake (1%+ supply). Иначе атакующий с небольшим количеством токенов может попытаться протолкнуть вредоносный proposal.
Timelock: обязательный delay перед исполнением финансовых операций. Минимум 48 часов, для крупных сумм — 7 дней. Это даёт сообществу время заметить и отреагировать.
Spending caps: даже через governance — не позволять выводить более X% treasury за один proposal. Требовать разбивки крупных трат.
Veto mechanism: некоторые DAO (Compound, Uniswap) имеют Security Council — малый мультисиг с правом вето на governance решения в течение timelock периода. Используется только для явно вредоносных proposals.
// Guardian / veto механизм
contract TreasuryGuardian {
address public immutable securityCouncil; // 4/7 мультисиг
TimelockController public immutable timelock;
// Отмена вредоносного proposal во время timelock периода
function vetoOperation(bytes32 operationId) external {
require(msg.sender == securityCouncil, "Not security council");
timelock.cancel(operationId);
emit OperationVetoed(operationId);
}
}
Treasury — это runway DAO. Правильно управляемое казначейство позволяет протоколу пережить медвежий рынок и продолжать разработку независимо от цены нативного токена.







