Разработка мобильного приложения для DAO (голосование, предложения)
Мы создаём мобильные приложения для DAO, которые превращают чтение предложений и голосование в простой, интуитивный процесс. Держатели токенов хотят быстро узнать, какие решения принимаются, проголосовать в пару кликов и не пропустить дедлайн. Наш опыт — 5+ лет разработки мобильных Web3-решений, включая интеграцию с Governor и Snapshot, делегирование токенов и аналитику. Это позволяет сократить время разработки на 40% и избежать типичных ошибок на стыке блокчейна и мобильной платформы.
Главная техническая сложность — поддержка двух принципиально разных стандартов голосования: on-chain (OpenZeppelin Governor) и off-chain (Snapshot). Каждый требует своей архитектуры запросов, подписи транзакций и обработки состояний. Мы комбинируем их так, чтобы пользователь получал единый интерфейс без лишних действий.
Как работают стандарты голосования: Governor vs Snapshot
On-chain (Governor): Все транзакции в блокчейне. OpenZeppelin Governor — стандарт де-факто, используемый Compound, Uniswap, Aave. Голосование стоит газ, но результаты криптографически верифицируемы. Поддерживает делегирование через ERC20Votes.
Off-chain (Snapshot): Подписи EIP-712 без газа. Голосование хранится в сети Snapshot, но не исполняется автоматически — нужен multisig или исполнитель. Экономия на газе до 100%.
Большинство DAO комбинируют: Snapshot для предварительного голосования (без газа), Governor для исполнения. Приложение должно работать с обоими через единый интерфейс.
Как реализовать голосование on-chain и off-chain?
Пошаговый процесс для интеграции двух стандартов:
- Определите тип голосования в зависимости от критичности и бюджета: on-chain для ответственных решений, Snapshot для быстрых опросов.
- Для on-chain используйте
governor.castVote(proposalId, support)с параметрами 0 (Against), 1 (For), 2 (Abstain). Транзакция требует газа. - Для off-chain подпишите сообщение EIP-712 и отправьте через 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 hash предложения let choice: Int // 1 = For, 2 = Against let metadata: String // "{}" } } Подпись через WalletConnect или встроенный кошелёк.
Список предложений: данные и состояния
Предложение проходит через несколько состояний (см. таблицу). Данные получаем через The Graph (субграф OpenZeppelin Governor) или Tally API — прямой вызов IGovernor.state() для списка неэффективен.
| Состояние | Governor | Описание |
|---|---|---|
| Pending | 0 | Ещё не началось голосование |
| Active | 1 | Голосование открыто |
| Canceled | 2 | Отменено автором |
| Defeated | 3 | Не набрало кворум или большинство |
| Succeeded | 4 | Прошло, ожидает постановки в очередь |
| Queued | 5 | В TimeLock, ожидает исполнения |
| Expired | 6 | Истёк дедлайн исполнения |
| Executed | 7 | Исполнено |
Для запроса списка используем GraphQL-запросы к Tally API:
struct TallyProposalsQuery: Codable { 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 — готовые агрегаторы. Для собственного DAO разворачиваем субграф The Graph.
Почему экран предложения — главная точка онбординга?
Большинство участников только голосуют, не создают предложения. Экран должен показывать статус, таймер, результаты в реальном времени и кнопку голосования (только в Active). Пример структуры:
- Заголовок и описание (рендерим Markdown)
- Статус и временные рамки
- Результаты: For / Against / Abstain с прогресс-барами
- Кворум: набрано X из Y голосов
- Кнопки голосования (только в Active)
- История событий: кто проголосовал и когда
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 } Делегирование голосов: что и как проверять
Токены ERC20Votes позволяют делегировать голосующую силу без передачи токенов. Вызов token.delegate(delegateeAddress) — одна транзакция. Пока не вызвана — токены не участвуют в голосовании Governor, даже если на балансе.
UX: при первом входе проверяем делегирование через token.delegates(account). Если не делегировано — показываем баннер «Активируйте право голоса». Пример кода:
func getDelegatee(for address: EthereumAddress) async throws -> EthereumAddress { return try await governanceToken.delegates(account: address) } Push-уведомления для активных участников
Подписываем на события через APNs/FCM: новое предложение, скорое закрытие голосования (за 24 часа), кворум набран, предложение исполнено. Пользователь настраивает фильтры — не спамим.
Создание предложения: когда это нужно
Создание предложения через Governor требует указания targets, values, calldatas и description. Интерфейс упрощаем через шаблоны или конструктор вызовов для технически грамотных. Проверяем минимальный порог токенов (proposalThreshold) до отправки.
Типичные ошибки при интеграции
- Не проверять делегирование — голоса не учитываются.
- Отправлять подпись Snapshot без корректного EIP-712 domain.
- Игнорировать кворум: без него предложение не исполнится.
- Спамить уведомлениями — пользователи отключают их.
Что входит в работу
- Техническая документация по интеграции с Governor и Snapshot
- Исходный код модулей голосования и делегирования
- Настройка push-уведомлений и аналитики
- Доступ к репозиторию и CI/CD
- Обучение команды по доработкам и поддержка 2 недели
Сроки
| Компонент | Срок |
|---|---|
| Список предложений + статусы | 1 неделя |
| Экран предложения с real-time голосами | 1 неделя |
| On-chain голосование (Governor) | 3 дня |
| Snapshot голосование | 3 дня |
| Делегирование токенов | 2 дня |
| Push-уведомления | 3 дня |
| Создание предложения | 1 неделя |
MVP (список + голосование + делегирование): 3–4 недели. Полное приложение с созданием предложений, историей, аналитикой: 8–12 недель.
Обратитесь к нам для детального обсуждения вашего DAO-приложения. Получите консультацию инженеров с опытом более 5 лет в мобильной разработке и 30+ реализованных Web3-проектов. Мы гарантируем соблюдение App Store Review Guidelines и безопасность приватных ключей. Закажите разработку и сократите время выхода на рынок на 40%.







