Розроблення мобільних додатків для DAO (голосування, пропозиції)
DAO-додаток — це інтерфейс до смарт-контракту управління. Власники токенів створюють пропозиції, голосують, делегують голоси, стежать за виконанням. На відміну від NFT або DeFi, UX тут орієнтований на читання: більшість користувачів голосує, а не створює пропозиції. Екран списку пропозицій — головна точка входу.
Стандарти: OpenZeppelin Governor vs. Snapshot
Два принципово різні підходи:
On-chain (Governor): все відбувається в блокчейні. OpenZeppelin Governor — стандарт де-факто. Голосування коштує газ, але результати криптографічно верифіковані. GovernorBravo, GovernorAlpha — форки використовують Compound, Uniswap, Aave.
Off-chain (Snapshot): підписи EIP-712 без транзакцій. Голосування безкоштовне, зберігається в мережі Snapshot. Результати не виконуються автоматично — потребує multisig або виконавця.
Більшість DAO комбінують обидва: Snapshot для голосування (без газу), Governor для виконання прийнятих пропозицій. Додаток має підтримувати обидва.
Список пропозицій: дані та стани
Пропозиція проходить через кілька станів:
| Стан | Governor | Описання |
|---|---|---|
| Pending | 0 | Голосування ще не почалось |
| Active | 1 | Голосування відкрите |
| Canceled | 2 | Скасовано автором |
| Defeated | 3 | Не набрало кворум чи більшість |
| Succeeded | 4 | Пройшло, чекає запису в чергу |
| Queued | 5 | У TimeLock, чекає виконання |
| Expired | 6 | Дедлайн виконання істік |
| Executed | 7 | Виконано |
Отримуйте дані через IGovernor.state(proposalId). Отримуйте список пропозицій через The Graph або Tally API. Прямі виклики контракту неефективні: немає нативного методу getProposals().
// iOS — отримання пропозицій через Tally GraphQL API
struct TallyProposalsQuery: Codable {
// GraphQL запит для отримання пропозицій DAO
static let query = """
query Proposals($governorId: ID!, $first: Int!) {
proposals(
governorId: $governorId,
pagination: { first: $first },
sort: { sortBy: id, isDescending: true }
) {
id
title
description
status
voteStats { support votes percent }
start { ... on Block { timestamp } }
end { ... on Block { timestamp } }
}
}
"""
}
Tally, Boardroom, Messari Governance — агрегатори з API для більшості крупних DAO. Для власних DAO — The Graph з субграфом OpenZeppelin Governor.
Екран пропозиції
Структура екрану:
- Заголовок та описання (Markdown → відрендерений текст)
- Статус та часові рамки (початок, конец, таймер)
- Результати у реальному часі: For / Against / Abstain з відсотками та прогресс-барами
- Кворум: набрано X з Y голосів (прогрес до порога)
- Кнопки голосування (лише в статусі Active)
- Історія подій: хто голосував, коли
// Android — відображення результатів голосування
data class ProposalVoteStats(
val forVotes: BigDecimal,
val againstVotes: BigDecimal,
val abstainVotes: BigDecimal
) {
val totalVotes get() = forVotes + againstVotes + abstainVotes
val forPercent get() = if (totalVotes > BigDecimal.ZERO)
(forVotes / totalVotes * BigDecimal(100)).toInt() else 0
val quorumReached get() = totalVotes >= quorumThreshold
}
Голосування: On-chain та Off-chain (Snapshot)
On-chain: governor.castVote(proposalId, support) — три значення support: 0 (Against), 1 (For), 2 (Abstain). З причиною: castVoteWithReason(proposalId, support, reason). Транзакція коштує газ.
Snapshot off-chain: підпис повідомлення EIP-712, відправлено в Snapshot API:
// iOS — голосування через Snapshot API (без транзакції)
struct SnapshotVotePayload: Codable {
let version: String // "0.1.3"
let timestamp: Int
let space: String // ID простору DAO
let type: String // "single-choice", "approval", "quadratic"
let payload: VotePayload
struct VotePayload: Codable {
let proposal: String // IPFS хеш пропозиції
let choice: Int // 1 = For, 2 = Against
let metadata: String // "{}"
}
}
Підпишіть через WalletConnect або вбудований гаманець. Безкоштовне — головна перевага Snapshot.
Делегування голосів
Багато токенів підтримують делегування (ERC20Votes): власники можуть передати голосуючу силу іншій адресі без передачі токенів.
token.delegate(delegateeAddress) — одна транзакція. Доки не викликано — токени не беруть участь у голосуванні Governor, навіть якщо на балансі.
UX: при першому запуску DAO-додатку перевірте, чи делеговані токени. Якщо ні — баннер «Активуйте право голосу: делегуйте токени (собі або іншому)».
// iOS — перевірка статусу делегування
func getDelegatee(for address: EthereumAddress) async throws -> EthereumAddress {
return try await governanceToken.delegates(account: address)
}
// Якщо результат == .zero або == address — ще не делеговано
Push-уведомлення для активних учасників
- Створено нову пропозицію
- Голосування скоро закривається (за 24 години)
- Пропозиція пройшла / відхилена
- Пропозиція поставлена на виконання
- Кворум набран
Дозвольте користувачам налаштувати, які уведомлення отримувати — не спамте всіма подіями.
Створення пропозиції
Створення пропозиції Governor — складна операція: необхідно вказати targets (адреси контрактів), values (ETH), calldatas (закодовані виклики функцій) та description. Інтерфейс має або спростити через шаблони, або надати конструктор викликів для технічних користувачів.
Перевірте мінімальний поріг токенів (proposalThreshold) заздалегідь та показуйте, чи у користувача достатньо токенів.
Часова шкала
| Компонент | Часова шкала |
|---|---|
| Список пропозицій + статуси | 1 тиждень |
| Екран пропозиції з голосами у реальному часі | 1 тиждень |
| On-chain голосування (Governor) | 3 дні |
| Snapshot голосування | 3 дні |
| Делегування голосів | 2 дні |
| Push-уведомлення | 3 дні |
| Створення пропозиції | 1 тиждень |
MVP (список + голосування + делегування): 3–4 тижні. Повний додаток з створенням пропозицій, історією, аналітикою: 8–12 тижнів.







