DAO Voting
On-chain governance with token-weighted proposals and vote delegation
Pending
Anchor 0.32
Program ID
6wGk7DpNBYDgwW4JZoRjdLB6iNni2AzhyuhiFsgES9ax
Repository
ExpertVagabond/solana-dao-voting
Binary Size
318K
Cluster
Devnet
Instructions
IX
create_dao
Initializes a new DAO with a governance token, quorum threshold, and voting period.
| Parameter | Type | Description |
|---|---|---|
name | String | Name of the DAO (max 32 characters) |
quorum | u64 | Minimum total votes required for a proposal to pass |
voting_period | i64 | Duration of voting in seconds |
IX
create_proposal
Creates a new governance proposal for the DAO members to vote on.
| Parameter | Type | Description |
|---|---|---|
title | String | Title of the proposal (max 64 characters) |
description_hash | [u8; 32] | SHA-256 hash of the full proposal description (stored off-chain) |
IX
cast_vote
Casts a token-weighted vote on an active proposal.
| Parameter | Type | Description |
|---|---|---|
vote | bool | Vote direction: true for yes, false for no |
amount | u64 | Number of governance tokens to vote with |
IX
execute_proposal
Executes a proposal that has passed quorum after the voting period ends.
| Parameter | Type | Description |
|---|---|---|
| No parameters | ||
IX
withdraw_vote
Withdraws locked governance tokens after the voting period has ended.
| Parameter | Type | Description |
|---|---|---|
| No parameters | ||
Accounts
Dao
| Field | Type | Description |
|---|---|---|
authority | Pubkey | DAO creator and admin |
mint | Pubkey | Governance token mint |
name | String | Name of the DAO |
quorum | u64 | Minimum votes for proposal passage |
voting_period | i64 | Duration of each voting window in seconds |
proposal_count | u64 | Total proposals created |
bump | u8 | PDA bump seed |
Proposal
| Field | Type | Description |
|---|---|---|
dao | Pubkey | The DAO this proposal belongs to |
proposer | Pubkey | Wallet that submitted the proposal |
id | u64 | Sequential proposal ID |
title | String | Proposal title |
description_hash | [u8; 32] | Hash of the off-chain description |
yes_votes | u64 | Total tokens voted yes |
no_votes | u64 | Total tokens voted no |
created_at | i64 | Unix timestamp when the proposal was created |
executed | bool | Whether the proposal has been executed |
bump | u8 | PDA bump seed |
VoteRecord
| Field | Type | Description |
|---|---|---|
proposal | Pubkey | The proposal this vote is for |
voter | Pubkey | Wallet that cast the vote |
amount | u64 | Number of tokens committed |
vote | bool | Vote direction (true = yes) |
bump | u8 | PDA bump seed |
PDA Derivation
dao
seeds = [b"dao", authority, mint]
proposal
seeds = [b"proposal", dao, proposal_id.to_le_bytes()]
vote
seeds = [b"vote", proposal, voter]
Error Codes
NameTooLong
DAO name exceeds the maximum 32 character limit
TitleTooLong
Proposal title exceeds the maximum 64 character limit
VotingEnded
The voting period for this proposal has already ended
VotingNotEnded
Cannot execute or withdraw before the voting period ends
QuorumNotReached
The proposal did not receive enough votes to reach quorum
AlreadyExecuted
This proposal has already been executed
Overflow
Arithmetic overflow in vote tallying
Usage Example
import { Program, BN } from "@coral-xyz/anchor"; import { PublicKey } from "@solana/web3.js"; // Derive the DAO PDA const [daoPda] = PublicKey.findProgramAddressSync( [Buffer.from("dao"), authority.publicKey.toBuffer(), mint.toBuffer()], program.programId ); // Create a DAO with 1M token quorum and 3-day voting await program.methods .createDao( "My Protocol DAO", new BN(1_000_000), // quorum new BN(3 * 86400) // voting_period (3 days) ) .accounts({ authority: authority.publicKey, mint, dao: daoPda, }) .signers([authority]) .rpc(); // Cast a vote on proposal #0 const proposalId = new BN(0); const [proposalPda] = PublicKey.findProgramAddressSync( [Buffer.from("proposal"), daoPda.toBuffer(), proposalId.toArrayLike(Buffer, "le", 8)], program.programId ); await program.methods .castVote(true, new BN(500_000)) .accounts({ proposal: proposalPda, voter: voter.publicKey, }) .signers([voter]) .rpc();