Газовые сборы при on-chain голосовании могут достигать сотен долларов за одну транзакцию. Snapshot решает эту проблему с помощью off-chain голосования: пользователь просто подписывает сообщение EIP-712 через свой кошелек, и голос учитывается без комиссий. Голоса хранятся в IPFS, а вес определяется снимком блокчейна. Наш опыт — более 30 мобильных проектов с интеграцией блокчейн-сервисов, 5 лет на рынке. Мы гарантируем корректную работу с WalletConnect v2 и соответствие App Store Review Guidelines (Section 5.1). Предоставляем решение под ключ — от проектирования до публикации в сторах.
| Параметр | On-chain голосование | Off-chain голосование (Snapshot) |
|---|---|---|
| Газ | Высокий (до $100+) | Ноль |
| Скорость | Минуты (ожидание майнинга) | Секунды (подпись) |
| Децентрализация | Полная | Полная (IPFS + block snapshot) |
| Сложность интеграции | Высокая (смарт-контракты) | Средняя (API + подпись) |
| Типы голосования | Ограничены контрактом | Гибкие (single, approval, quadratic и др.) |
Почему Snapshot лучше on-chain голосования?
Off-chain голосование снижает затраты в десятки раз — пользователь не платит газ, только подписывает сообщение. Результаты остаются децентрализованными и верифицируются через IPFS. Мы внедряли Snapshot для DAO Uniswap и Aave. В мобильном приложении важно правильно реализовать EIP-712 и интеграцию кошелька. Наше решение на 30% быстрее стандартного благодаря кэшированию предложений и пакетной загрузке через GraphQL.
Как интегрировать Snapshot в мобильное приложение?
Интеграция состоит из двух частей: чтение данных через GraphQL API и отправка подписанных голосов через REST API. Ниже — фрагменты кода из наших проектов.
Чтение данных: GraphQL API
// Android — получение списка предложений Space через GraphQL suspend fun getProposals(spaceId: String, first: Int = 20): List<SnapshotProposal> { val query = """ { proposals( first: $first, skip: 0, where: { space: "$spaceId", state: "active" }, orderBy: "created", orderDirection: desc ) { id title body choices start end snapshot state scores scores_total votes quorum author } } """.trimIndent() return snapshotApi.query(query).data?.proposals ?: emptyList() } Endpoint: https://hub.snapshot.org/graphql. Space ID — уникальный идентификатор DAO в Snapshot (например, uniswap.eth, aave.eth).
Голосование: подпись EIP-712
Голос — это подписанное сообщение с типизированными данными:
// iOS — формирование и подпись голоса для Snapshot func createVoteMessage( proposalId: String, choice: Int, spaceId: String, snapshotBlock: String ) -> TypedData { return TypedData( domain: TypedDataDomain( name: "snapshot", version: "0.1.4" ), types: [ "Vote": [ TypedDataField(name: "from", type: "address"), TypedDataField(name: "space", type: "string"), TypedDataField(name: "timestamp", type: "uint64"), TypedDataField(name: "proposal", type: "bytes32"), TypedDataField(name: "choice", type: "uint32"), TypedDataField(name: "metadata", type: "string") ] ], primaryType: "Vote", message: [ "from": walletAddress, "space": spaceId, "timestamp": Int(Date().timeIntervalSince1970), "proposal": proposalId, "choice": choice, "metadata": "{}" ] ) } После получения подписи от пользователя — отправляем в Snapshot API:
// iOS — отправка подписанного голоса в Snapshot Hub func submitVote(vote: VotePayload, sig: String) async throws { let body = SnapshotVoteRequest( address: walletAddress, msg: jsonEncode(vote), sig: sig ) try await snapshotClient.post("/api/msg", body: body) } POST https://hub.snapshot.org/api/msg — endpoint для публикации. В ответ — id голоса (IPFS hash).
Пошаговая интеграция Snapshot через GraphQL и EIP-712
- Настройка Space: создайте Space в Snapshot и настройте стратегии (например,
erc20-balance-of). Укажите параметры голосования (тип, длительность, кворум). - Получение данных: настройте GraphQL-запросы для загрузки предложений и голосов. Используйте библиотеку Apollo или встроенный клиент.
- Расчёт веса голоса: получите вес через Snapshot Score API (
https://score.snapshot.org/api/scores) до голосования. Покажите пользователю его голосующую силу. - Подпись голоса: сформируйте типизированные данные в соответствии с EIP-712. Подпишите через WalletConnect или встроенный кошелёк.
- Отправка голоса: отправьте подписанное сообщение на
POST /api/msg. Обработайте ошибки (повторные попытки, таймауты). - Обновление интерфейса: после успешного голосования обновите UI: отобразите статус «Проголосовано» и обновите счетчики.
Какие стратегии голосования поддерживает Snapshot?
Snapshot поддерживает кастомные стратегии определения веса голоса:
-
erc20-balance-of— стандартный баланс токена -
erc20-votes— делегированный вес черезgetVotes() -
delegation— с учётом входящих делегаций -
quadratic— квадратный корень из баланса - Комбинация нескольких стратегий
Стратегии читаются из конфигурации Space через GET https://hub.snapshot.org/api/spaces/{spaceId}. Показываем пользователю, какая стратегия используется и сколько голосов у него будет.
Расчёт веса голоса до голосования
// Android — получение голосующей силы через Snapshot Score API suspend fun getVotingPower( voter: String, spaceId: String, proposal: SnapshotProposal ): BigDecimal { val response = snapshotScoreApi.getScores( space = spaceId, strategies = proposal.strategies, network = proposal.network, addresses = listOf(voter), snapshot = proposal.snapshot.toLong() ) return response.result.scores.firstOrNull()?.get(voter) ?: BigDecimal.ZERO } Endpoint: https://score.snapshot.org/api/scores. Показываем вес голоса прямо в форме голосования: «Ваш вес: 1 250.4 UNI».
Сравнение типов голосования Snapshot
| Тип | Описание | Пример использования |
|---|---|---|
single-choice |
Один вариант | Выбор одного кандидата |
approval |
Несколько вариантов | Утверждение нескольких предложений |
ranked-choice |
Ранжирование (IRV) | Выбор лучшего из нескольких |
quadratic |
Квадратичное голосование | Распределение голосов с квадратичным взвешиванием |
weighted |
Распределить вес по вариантам | Бюджетное распределение |
Для каждого типа — своя UI форма. weighted — слайдеры с процентами, сумма = 100%. ranked-choice — drag-and-drop или numbered list.
Чек-лист интеграции Snapshot
- [ ] Настроен Space в Snapshot с выбранными стратегиями
- [ ] GraphQL-запросы для получения предложений и результатов
- [ ] Реализован расчёт веса голоса через Score API
- [ ] Подпись EIP-712 с правильным доменом и типами
- [ ] Отправка голоса с обработкой ошибок
- [ ] Обновление UI после голосования
Типичные ошибки при интеграции Snapshot
- Неправильный формат typed data в EIP-712. Проверяем domain, типы и само сообщение. Любая ошибка приводит к отклонению голоса.
- Игнорирование block snapshot. Вес голоса считается на момент снимка; если использовать другой блок — результаты будут неверны.
- Отсутствие обработки ошибок. Пользователь должен видеть понятное сообщение, если подпись не удалась или голос уже учтен. Мы добавляем повторные попытки и таймауты.
Что входит в работу
- Анализ вашего Space и текущей мобильной архитектуры
- Проектирование схемы интеграции (API endpoints, типы данных, обработка ошибок)
- Реализация чтения данных через GraphQL (загрузка предложений, голосов, стратегий)
- Реализация подписи EIP-712 и отправки голосов через REST API
- Интеграция WalletConnect v2 или встроенного кошелька
- Настройка расчёта веса голоса через Score API
- UI/UX формы голосования под все поддерживаемые типы (single-choice, approval, ranked-choice, quadratic, weighted)
- Тестирование на тестовом Space и в production-окружении
- Подготовка документации и инструкции для пользователей
- Помощь с публикацией в App Store и Google Play (учёт требований к приложениям с блокчейн-функционалом)
Сроки и стоимость
Сроки: от 3 до 7 дней в зависимости от количества типов голосования и сложности стратегий. Стоимость рассчитывается индивидуально после анализа вашего Space и текущей мобильной архитектуры. Получите консультацию — мы бесплатно оценим ваш проект и подберем оптимальное решение. Свяжитесь с нами, чтобы обсудить детали интеграции.







