Voting Example
Pre-Alpha Disclaimer: This is an early pre-alpha release for exploring the SDK and starting development only. There is no real MPC signing — all signatures are generated by a single mock signer, not a distributed network. Do not submit any real transactions for signing or rely on any security guarantees. The dWallet keys, trust model, and signing protocol are not final; do not rely on any key material until mainnet. All interfaces, APIs, and data formats are subject to change without notice. The Solana program and all on-chain data will be wiped periodically and everything will be deleted when we transition to Ika Alpha 1. This software is provided “as is” without warranty of any kind; use is entirely at your own risk and dWallet Labs assumes no liability for any damages arising from its use.
Overview
A governance program where anyone can create proposals referencing a dWallet. Voters cast yes/no votes, and when the quorum of yes votes is reached, the program automatically CPI-calls approve_message on the dWallet program to authorize signing.
Use case: DAO governance, treasury management, group signing authorization.
Program Design
Accounts
| Account | Seeds | Size | Description |
|---|---|---|---|
| Proposal | ["proposal", proposal_id] | 195 bytes | Stores vote counts, quorum, message hash, dWallet reference |
| VoteRecord | ["vote", proposal_id, voter] | 69 bytes | Prevents double voting — one per voter per proposal |
Instructions
| Disc | Instruction | Description |
|---|---|---|
0 | CreateProposal | Create a proposal with message hash, quorum, and dWallet reference |
1 | CastVote | Record a vote; when quorum reached, CPI-calls approve_message |
Proposal Layout (195 bytes)
disc(1) + version(1) + proposal_id(32) + dwallet(32) + message_hash(32) +
user_pubkey(32) + signature_scheme(1) + creator(32) + yes_votes(4) +
no_votes(4) + quorum(4) + status(1) + message_approval_bump(1) +
bump(1) + _reserved(16)
Key Offsets
| Field | Offset | Size | Type |
|---|---|---|---|
| yes_votes | 163 | 4 | u32 LE |
| no_votes | 167 | 4 | u32 LE |
| quorum | 171 | 4 | u32 LE |
| status | 175 | 1 | 0=Open, 1=Approved |
CPI Flow
When yes_votes >= quorum, the program:
- Reads message hash, user pubkey, and signature scheme from the Proposal account
- Constructs a
DWalletContextwith the CPI authority PDA - Calls
ctx.approve_message(...)which creates aMessageApprovalPDA on the dWallet program - Sets proposal status to Approved
The caller then sends a gRPC Sign request with ApprovalProof::Solana { transaction_signature, slot } to obtain the actual signature.
E2E Flow
1. gRPC DKG → dWallet created on-chain, authority = caller
2. Transfer authority → CPI PDA owns the dWallet
3. Create proposal → quorum=3, message="Transfer 100 USDC"
4. Cast 3 YES votes → last vote triggers approve_message CPI
5. Verify approval → MessageApproval PDA exists, status=Pending
6. gRPC presign → allocate presign
7. gRPC sign → 64-byte signature returned
Testing
# Mollusk (instruction-level, no infrastructure needed)
cargo test -p ika-example-voting-pinocchio --test mollusk
cargo test -p ika-example-voting-native --test mollusk
# TypeScript E2E (requires validator + mock)
cd chains/solana/examples/voting/e2e && bun main.ts <DWALLET_ID> <VOTING_ID>
Source Files
- Pinocchio:
chains/solana/examples/voting/pinocchio/src/lib.rs - Native:
chains/solana/examples/voting/native/src/lib.rs - Anchor:
chains/solana/examples/voting/anchor/src/lib.rs - TypeScript E2E:
chains/solana/examples/voting/e2e/main.ts