Skip to content

[Feature] Implement DAO-style governance voting for protocol-level decisions and pool parameter changes #207

Description

@Sendi0011

Getting Started

  1. Fork the repository: https://github.com/JointSave-org/Joint_Save
  2. Clone your fork:
git clone https://github.com/<your-username>/Joint_Save.git
cd Joint_Save
  1. Create a new branch:
git checkout -b feat/dao-governance-voting

Overview

JointSave currently has no mechanism for pool members to collectively decide on pool parameters (e.g., changing deposit amounts, extending deadlines, or modifying penalty rules). All administrative decisions are made unilaterally by the pool creator/admin. This creates a single point of failure and does not reflect how real-world savings groups operate — where decisions are made collectively.

The roadmap (Phase 3) lists "DAO Governance — Community-driven protocol improvements" but no foundation exists. The factory contract has no governance extensions. The frontend has no voting UI.

This issue implements a lightweight on-chain governance system that allows pool members to propose and vote on parameter changes, with execution only occurring when a configurable quorum is met.

Requirements

Smart Contract Changes

  • New contract: smartcontract/governance/
    • Storage:
      • proposals: Map<Bytes, Proposal> where Proposal contains:
        • proposer: Address
        • proposal_type: ProposalType (enum: ChangeDepositAmount, ExtendDeadline, AddPenalty, RemovePenalty, ChangeQuorum, Custom)
        • parameters: Map<String, Bytes> (serialized proposal-specific params)
        • votes_for: Vec<Address>
        • votes_against: Vec<Address>
        • status: ProposalStatus (enum: Active, Passed, Executed, Expired, Rejected)
        • created_at: u64
        • expires_at: u64 (48 hours default)
        • execution_result: Option<Vec<u8>>
      • voting_quorum: u32 (percentage of total members, e.g., 51 means 51%)
      • pool_contract: Address (the pool this governance contract is attached to)
    • Functions:
      • create_proposal(member, proposal_type, parameters) -> proposal_id — only pool members can create; max 3 active proposals per pool
      • vote(proposer, proposal_id, in_favor: bool) — one vote per member per proposal; cannot vote on own proposal
      • execute_proposal(executor, proposal_id) — only succeeds if votes_for >= quorum percentage of total members AND proposal is not expired; executes the parameter change on the target pool contract via CPI
      • get_proposal(proposal_id) -> Proposal
      • get_active_proposals(pool_contract) -> Vec<Proposal>
      • get_voting_quorum() -> u32

Pool Contract Integration

  • Add a new function to all three pool contracts: apply_governance_proposal(admin, proposal_type, parameters) — admin-only but designed to be called via CPI from the governance contract
  • The governance contract stores the pool contract address and can invoke apply_governance_proposal via env.invoke_contract()
  • Existing pool parameters that become governable:
    • deposit_amount (flexible pools)
    • round_duration (rotational pools)
    • penalty_percentage

Frontend Changes

  • New component: frontend/components/governance/proposal-list.tsx
    • Lists all active and recent proposals for a pool
    • Shows proposal type, proposer, vote counts, time remaining
    • Color-coded status badges (Active=blue, Passed=green, Executed=gray, Expired=orange, Rejected=red)
  • New component: frontend/components/governance/create-proposal-dialog.tsx
    • Modal dialog accessible from group detail page (admin section or governance tab)
    • Dropdown for proposal type
    • Dynamic form fields based on selected type (e.g., "New Deposit Amount" shows amount input)
    • Character limit on proposal description (500 chars)
  • New component: frontend/components/governance/vote-card.tsx
    • Shows proposal details with "Vote For" and "Vote Against" buttons
    • Visual progress bar showing votes toward quorum
    • Disable vote buttons if user already voted or is the proposer
    • "Execute" button visible only when quorum is met and user is admin
  • New tab or section in group detail page: "Governance" tab (visible when governance contract is deployed for the pool)
  • New hook: frontend/hooks/useGovernance.ts
    • Fetches proposals, vote status, and quorum info for a pool
    • createProposal(), vote(), executeProposal() mutations
    • Realtime vote count updates via Supabase Realtime subscription on a new governance_votes table

Database Changes

  • New table governance_votes (off-chain mirror for realtime):
    • proposal_id (text)
    • pool_id (text)
    • voter_address (text)
    • vote (boolean)
    • created_at (timestamptz)
    • RLS: members of the pool can read; authenticated users can insert their own

Acceptance Criteria

  • Pool members can create proposals with different types and parameter sets
  • Members can vote For or Against exactly once per proposal
  • Proposals automatically expire after 48 hours
  • Execution only succeeds when quorum percentage of members have voted For
  • Executed proposals apply parameter changes to the target pool contract via CPI
  • Max 3 active proposals enforced per pool
  • Frontend renders proposal list, create dialog, and vote cards correctly
  • Vote counts update in realtime without page refresh
  • Governance tab only appears for pools with a governance contract attached
  • Mobile: voting UI is fully responsive and touch-friendly

Metadata

Metadata

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardThird CampaignCampaign: Third CampaignfeatureNew functionality to addhigh-complexityLarge scope, multiple systems/files. Needs planning

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions