Газовые сборы при 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 и текущей мобильной архитектуры. Получите консультацию — мы бесплатно оценим ваш проект и подберем оптимальное решение. Свяжитесь с нами, чтобы обсудить детали интеграции.







