Proposals

Understand the complete lifecycle of governance proposals in CredDAO.

Proposal Types

TypeDescriptionTier RequiredTime-Lock
StandardRegular governance decisionsGold+Per tier
ExpeditedTime-sensitive mattersPlatinum24 hours
EmergencyCritical situationsPlatinum + badges24 hours

Proposal States

StateDescription
DraftInitial state (reserved for future use)
VotingActive voting period
SucceededPassed voting, awaiting time-lock
DefeatedFailed to pass
ExecutedSuccessfully executed on-chain
CancelledCancelled by proposer

Proposal Account Structure

#[account]
pub struct ProposalAccount {
    pub dao_config: Pubkey,        // Reference to DAO config
    pub proposer: Pubkey,          // Proposal creator
    pub proposal_type: ProposalType,
    pub state: ProposalState,
    pub for_votes: u64,            // Total votes for
    pub against_votes: u64,        // Total votes against
    pub abstain_votes: u64,        // Abstain votes
    pub voting_start: i64,         // Unix timestamp
    pub voting_end: i64,           // Voting deadline
    pub time_lock_expiry: i64,     // Execution available after
    pub quorum_required: u64,      // Minimum participation
    pub total_voting_power: u64,   // Total power at creation
    pub executed_at: Option<i64>,  // Execution timestamp
    pub bump: u8,
}

Creating Proposals

Requirements

Standard

  • Gold or Platinum tier
  • 30+ active days
  • Sufficient SOL for rent

Expedited

  • Platinum tier only
  • 30+ active days
  • Sufficient SOL for rent

Emergency

  • Platinum tier
  • Required badge count
  • 30+ active days
  • DAO authority approval (optional)

SDK Example

const proposal = await client.createProposal({
  proposalType: ProposalType.Standard,
  title: 'Treasury Allocation',
  description: 'Allocate funds for development...',
});

Voting on Proposals

Vote Types

enum VoteType {
  For = 0,
  Against = 1,
  Abstain = 2,
}

Voting Power

Your voting power is locked at the time of voting:

// In cast_vote
let voting_power = calculate_voting_power(voter.delegated_power, voter.fairscore);
 
vote_record.voting_power = voting_power;
vote_record.fairscore_at_vote = voter.fairscore;
Note:

Your FairScore at the time of voting is recorded. This prevents last-minute score manipulation.

Quorum Requirements

Proposals require minimum participation:

let total_votes = proposal.for_votes + proposal.against_votes + proposal.abstain_votes;
let quorum_reached = total_votes >= proposal.quorum_required;

Quorum is calculated as:

quorumRequired = totalVotingPower × quorumPercentage / 100

Finalization

After voting ends, anyone can finalize:

pub fn finalize_proposal(ctx: Context<FinalizeProposal>) -> Result<()> {
    require!(
        clock.unix_timestamp >= proposal.voting_end,
        CredDAOError::ProposalVotingActive
    );
 
    let total_votes = proposal.for_votes + proposal.against_votes + proposal.abstain_votes;
    
    if total_votes < proposal.quorum_required {
        proposal.state = ProposalState::Defeated;
    } else {
        proposal.state = if proposal.for_votes > proposal.against_votes {
            ProposalState::Succeeded
        } else {
            ProposalState::Defeated
        };
    }
}

Execution

After time-lock expires:

await client.executeProposal(proposalAddress);

Requirements:

  • Proposal state is Succeeded
  • Current time >= time_lock_expiry

Events

ProposalCreated {
    proposal: Pubkey,
    proposer: Pubkey,
    proposal_type: ProposalType,
    voting_end: i64,
    time_lock_expiry: i64,
}
 
VoteCast {
    proposal: Pubkey,
    voter: Pubkey,
    vote: u8,
    voting_power: u64,
    fairscore: u64,
}
 
ProposalFinalized {
    proposal: Pubkey,
    state: ProposalState,
    for_votes: u64,
    against_votes: u64,
}
 
ProposalExecuted {
    proposal: Pubkey,
    executed_at: i64,
}

Best Practices

Before Creating

  • Discuss in community forums
  • Gather initial support
  • Prepare clear rationale
  • Consider timing

After Creating

  • Promote in community
  • Answer questions
  • Address concerns
  • Monitor vote progress