We create mobile apps for DAOs that turn reading proposals and voting into a simple, intuitive process. Token holders want to quickly learn about decisions being made, vote in a couple of clicks, and not miss deadlines. Our expertise—5+ years of mobile Web3 development, including integration with Governor and Snapshot, token delegation, and analytics—cuts development time by 40% and avoids common pitfalls at the intersection of blockchain and mobile platforms.
The main technical challenge is supporting two fundamentally different voting standards: on-chain (OpenZeppelin Governor) and off-chain (Snapshot). Each requires its own architecture for requests, transaction signing, and state handling. We combine them so the user gets a unified interface without extra steps.
How Voting Standards Work: Governor vs Snapshot
On-chain (Governor): All transactions are on the blockchain. OpenZeppelin Governor is the de facto standard used by Compound, Uniswap, Aave. Voting costs gas, but results are cryptographically verifiable. Supports delegation via ERC20Votes.
Off-chain (Snapshot): EIP-712 signatures without gas. Voting data is stored on the Snapshot network, but actions are not automatically executed—a multisig or executor is needed. Gas savings up to 100%.
Most DAOs combine: Snapshot for preliminary voting (no gas), Governor for execution. The app must work with both through a single interface.
How to Implement On-chain and Off-chain Voting?
Step-by-step process for integrating both standards:
- Determine the voting type based on criticality and budget: on-chain for important decisions, Snapshot for quick polls.
- For on-chain, use
governor.castVote(proposalId, support)with parameters 0 (Against), 1 (For), 2 (Abstain). The transaction requires gas. - For off-chain, sign an EIP-712 message and send it via the Snapshot API. Free—key advantage. Example implementation:
struct SnapshotVotePayload: Codable { let version: String // "0.1.3" let timestamp: Int let space: String // DAO space ID let type: String // "single-choice", "approval", "quadratic" let payload: VotePayload struct VotePayload: Codable { let proposal: String // IPFS hash of the proposal let choice: Int // 1 = For, 2 = Against let metadata: String // "{}" } } Sign via WalletConnect or a built-in wallet.
Proposal List: Data and States
A proposal goes through several states (see table). Data is obtained via The Graph (OpenZeppelin Governor subgraph) or Tally API—direct IGovernor.state() call is inefficient for a list.
| State | Governor | Description |
|---|---|---|
| Pending | 0 | Voting has not started yet |
| Active | 1 | Voting is open |
| Canceled | 2 | Canceled by author |
| Defeated | 3 | Did not reach quorum or majority |
| Succeeded | 4 | Passed, awaiting queue |
| Queued | 5 | In TimeLock, awaiting execution |
| Expired | 6 | Execution deadline passed |
| Executed | 7 | Executed |
To request a list, use GraphQL queries to 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—ready aggregators. For a custom DAO, we deploy a The Graph subgraph.
Why the Proposal Screen Is the Main Onboarding Point?
Most participants only vote, not create proposals. The screen should show status, timer, real-time results, and a vote button (only in Active). Example structure:
- Title and description (render Markdown)
- Status and time frame
- Results: For / Against / Abstain with progress bars
- Quorum: X of Y votes reached
- Voting buttons (only in Active)
- Event history: who voted and when
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 } Token Delegation: What and How to Check
ERC20Votes tokens allow delegating voting power without transferring tokens. Calling token.delegate(delegateeAddress) is one transaction. If not called, tokens don't participate in Governor voting even if on balance.
UX: on first login, check delegation via token.delegates(account). If not delegated, show a banner "Activate your voting right". Example code:
func getDelegatee(for address: EthereumAddress) async throws -> EthereumAddress { return try await governanceToken.delegates(account: address) } Push Notifications for Active Participants
Subscribe to events via APNs/FCM: new proposal, upcoming vote close (24h warning), quorum reached, proposal executed. User configures filters—we don't spam.
Creating a Proposal: When Needed
Creating a proposal via Governor requires specifying targets, values, calldatas, and description. We simplify the interface with templates or a call builder for technically proficient users. Check the minimum token threshold (proposalThreshold) before submission.
Typical Integration Mistakes
- Not checking delegation—votes are not counted.
- Sending Snapshot signature without correct EIP-712 domain.
- Ignoring quorum: without it, the proposal won't execute.
- Spamming notifications—users turn them off.
What's Included in the Work
- Technical documentation for Governor and Snapshot integration
- Source code for voting and delegation modules
- Push notification and analytics setup
- Repository access and CI/CD
- Team training for modifications and 2-week support
Timelines
| Component | Duration |
|---|---|
| Proposal list + statuses | 1 week |
| Proposal screen with real-time votes | 1 week |
| On-chain voting (Governor) | 3 days |
| Snapshot voting | 3 days |
| Token delegation | 2 days |
| Push notifications | 3 days |
| Proposal creation | 1 week |
MVP (list + voting + delegation): 3–4 weeks. Full app with proposal creation, history, analytics: 8–12 weeks.
Contact us for a detailed discussion of your DAO app. Get a consultation from engineers with over 5 years of mobile development experience and 30+ implemented Web3 projects. We guarantee compliance with App Store Review Guidelines and secure private key storage. Order development and reduce time-to-market by 40%.







