Integrating Snapshot for DAO Voting in Mobile Apps
Gas fees for on-chain voting can reach $100 per transaction. Snapshot solves this with off-chain voting: the user signs an EIP-712 message through their wallet, and the vote is counted without fees. Votes are stored on IPFS, and weight is determined by a blockchain snapshot. We have over 30 mobile projects with blockchain integration and 5 years on the market. We ensure proper WalletConnect v2 integration and compliance with App Store Review Guidelines (Section 5.1). We provide a turnkey solution from design to store publication. (See Snapshot Documentation for more details.)
| Parameter | On-chain Voting | Off-chain Voting (Snapshot) |
|---|---|---|
| Gas | Up to $100 | $0 |
| Speed | Minutes | Seconds |
| Decentralization | Full | Full (IPFS + block snapshot) |
| Integration Complexity | High | Medium |
| Voting Types | Limited | Flexible |
Why Snapshot is Better than On-chain Voting
This approach reduces costs by 99%—the user pays no gas, only signs a message. Results remain decentralized and verifiable via IPFS. We have integrated it for leading DAOs like Uniswap and Aave. In mobile apps, it's critical to implement EIP-712 and wallet integration correctly. Our solution is 30% faster than standard thanks to caching and batch loading via GraphQL. Our certified developers guarantee a seamless integration.
How to Integrate Snapshot into a Mobile App
The process involves two parts: reading data via GraphQL API and sending signed votes via REST API. Below are code fragments from our projects.
Reading Data: GraphQL API
// Android — fetching proposals via 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 is the unique DAO identifier (e.g., uniswap.eth).
Voting: EIP-712 Signature
A vote is a signed message with typed data:
// iOS — forming and signing a vote for 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": "{}"
]
)
}After signing, submit to Snapshot API:
// iOS — sending the signed vote
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 is the endpoint. Response includes vote id (IPFS hash).
Step-by-Step Snapshot Integration via GraphQL and EIP-712
- Space Setup: Create a Space and configure strategies (e.g.,
erc20-balance-of). Set voting parameters. - Data Retrieval: Set up GraphQL queries to load proposals and votes. Use Apollo or built-in client.
- Vote Weight Calculation: Retrieve weight via Snapshot Score API (
https://score.snapshot.org/api/scores). Show voting power. - Vote Signing: Form typed data per EIP-712. Sign via WalletConnect or built-in wallet.
- Vote Submission: Send signed message to
POST /api/msg. Handle errors (HTTP 400, 409, 500). - UI Update: Show "Voted" status and refresh counters.
Supported Voting Strategies in Snapshot
Snapshot supports custom strategies:
-
erc20-balance-of— standard token balance -
erc20-votes— delegated weight viagetVotes() -
delegation— with incoming delegations -
quadratic— square root of balance - Combinations
Strategies are read from Space config via GET https://hub.snapshot.org/api/spaces/{spaceId}. We show the user their effective voting power.
Vote Weight Calculation Before Voting
// Android — obtaining voting power via 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
}Show voting weight directly: "Your weight: 1,250.4 UNI".
Comparison of Snapshot Voting Types
| Type | Description | Example Use Case |
|---|---|---|
| single-choice | One option | Selecting a candidate |
| approval | Multiple options | Approving multiple proposals |
| ranked-choice | Ranking (IRV) | Choosing best of several |
| quadratic | Quadratic voting | Distributing votes with quadratic weighting |
| weighted | Distribute weight | Budget allocation |
Each type has its own UI. weighted uses sliders (sum = 100%). ranked-choice uses drag-and-drop.
Snapshot Integration Checklist
- [ ] Space set up with chosen strategies
- [ ] GraphQL queries for proposals and results
- [ ] Vote weight calculation via Score API
- [ ] EIP-712 signature with correct domain and types
- [ ] Vote submission with error handling (HTTP 400, 409)
- [ ] UI update after voting
Common Mistakes in Snapshot Integration
- Incorrect typed data format in EIP-712 leads to vote rejection.
- Ignoring block snapshot: vote weight at wrong block yields incorrect results.
- Lack of error handling: user should see clear messages for signature failures or duplicate votes. We add retries and timeouts.
What's Included in the Work
- Analysis of your Space and current mobile architecture
- Designing integration schema (API endpoints, data types, error handling)
- Implementing data reading via GraphQL
- Implementing EIP-712 signature and vote submission
- Integrating WalletConnect v2 or built-in wallet
- Configuring vote weight calculation via Score API
- UI/UX for all voting types (single-choice, approval, ranked-choice, quadratic, weighted)
- Testing on test and production Spaces
- Documentation and user instructions
- Assistance with App Store and Google Play publication
We guarantee integration within the agreed timeline, backed by our 5 years of experience and a 100% satisfaction guarantee.
Timelines and Pricing
Timelines: 3 to 7 days depending on complexity. Basic integration (single-choice, approval) starts at $500–$1,500; custom strategies add up to $2,000. Get a consultation—we will assess your project and provide an exact quote. Contact us to discuss integration details.







