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







