Skip to content

feat(governance): DAO-style governance voting for pool parameters - #249

Open
jajafwangshak86-ops wants to merge 1 commit into
JointSave-org:mainfrom
jajafwangshak86-ops:feat/dao-governance-voting
Open

feat(governance): DAO-style governance voting for pool parameters#249
jajafwangshak86-ops wants to merge 1 commit into
JointSave-org:mainfrom
jajafwangshak86-ops:feat/dao-governance-voting

Conversation

@jajafwangshak86-ops

Copy link
Copy Markdown

Closes #207

Summary

Implements a lightweight on-chain DAO governance system so pool members can propose and vote on parameter changes, with execution gated behind a configurable quorum.

Smart contracts (smartcontract/contracts/governance)

  • initialize(admin, pool_contract, voting_quorum) attaches governance to a pool (quorum = % of members)
  • create_proposal(member, type, description, parameters) — members only, max 3 active proposals, 48h expiry, 500-char description cap
  • vote(voter, id, in_favor) — one vote per member per proposal, proposers cannot vote; enough against-votes auto-rejects
  • execute_proposal(executor, id) — requires quorum of total members and unexpired window; applies the change to the target pool via CPI (env.invoke_contract)
  • expire_proposal, get_proposal, get_active_proposals(pool), get_recent_proposals, get_voting_quorum
  • 25 unit tests incl. full CPI flow against a mock pool contract

Pool contract integration

All three pools (flexible / rotational / target) gain:

  • set_governance_contract(admin, gov)
  • apply_governance_proposal(caller, type, value) — admin-only or via the registered governance contract (CPI), covering deposit_amount, round_duration/deadline, and penalty_percentage
  • New views: governance_contract(), penalty_percentage(), plus minimum_deposit() / deposit_amount() / round_duration()

CI now builds the governance wasm alongside the other four contracts.

Frontend

  • components/governance/{proposal-list,vote-card,create-proposal-dialog,governance-panel}.tsx — status badges (Active=blue, Passed=green, Executed=gray, Expired=orange, Rejected=red), quorum progress bar, dynamic form fields per proposal type with 500-char counter, execute button for admins on passed proposals
  • hooks/useGovernance.ts — proposals/quorum/member-count fetching + createProposal/vote/executeProposal mutations + Supabase Realtime subscription on the votes mirror for live counts without refresh
  • Contract-layer additions to useJointSaveContracts.ts (proposal ScVal parsing + tx builders)
  • lib/governance.ts pure helpers with 8 unit tests wired into test:unit; en/es translations

Database

  • supabase/migrations/20260824000000_governance_votes.sql: off-chain governance_votes mirror (unique per proposal+voter, RLS, realtime publication) and pools.governance_contract_id which gates the Governance section in the group page
  • POST /api/governance validates membership before writing the mirror

Verification

  • cargo test: 188 tests pass across the workspace; clippy clean for the new contract
  • pnpm lint, pnpm format:check, pnpm build, check-budget, test:unit (210), test:components (101) all green locally

Implements issue JointSave-org#207.

Smart contracts:
- New governance contract (contracts/governance): member-only proposal
  creation (max 3 active), 48h voting window, one-vote-per-member,
  against-quorum auto-rejection, quorum-gated execution via CPI to the
  target pool, ChangeQuorum self-application, expiry finalization.
- All three pool contracts (flexible, rotational, target) gain
  set_governance_contract + apply_governance_proposal entrypoints
  (callable by admin or the registered governance contract) covering
  deposit amount, round duration/deadline and penalty percentage, plus
  penalty_percentage/governance_contract views.
- CI builds the new contract's wasm alongside the others.

Frontend:
- Governance panel with proposal list (color-coded status badges),
  vote cards with quorum progress bar, and create-proposal dialog
  with dynamic per-type fields and 500-char description limit.
- useGovernance hook: on-chain reads/mutations + Supabase Realtime
  subscription on the governance_votes mirror for live vote counts.
- /api/governance endpoint maintaining the off-chain vote mirror.
- Pure governance helpers in lib/ with unit tests (registered in
  test:unit); i18n messages for en/es.

Supabase:
- governance_votes table with RLS + realtime publication.
- pools.governance_contract_id column gating the UI tab.
@Sendi0011
Sendi0011 self-requested a review August 25, 2026 07:55

@Sendi0011 Sendi0011 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review — DAO-Style Governance Voting

Well-structured implementation — a full Soroban governance contract, off-chain vote mirror, Supabase Realtime updates, and a clean UI with EN/ES translations. Two things to address before merge:

1. Empty catch block silently swallows mirror errors (Medium)

frontend/hooks/useGovernance.ts:955mirrorVote has catch {} that silently drops any error. If the Supabase mirror fails (network, RLS, etc.), the vote still succeeds on-chain but the realtime UI never updates. At minimum log the error so it's diagnosable:

} catch (err) {
  console.error("Governance mirror failed:", err)
}

2. No migration/version support in governance contract (Low)

smartcontract/contracts/governance/src/lib.rs — Unlike the rotational and flexible contracts which have migrate and VERSION tracking, the governance contract has no versioning. If you ever need to upgrade it (e.g., change MAX_ACTIVE_PROPOSALS or add proposal types), there's no safe migration path. Consider adding the same migrate pattern used in the other contracts.

What is good

  • Governance contract is solid: admin auth, quorum checks (percentage-based), reject-on-against-threshold, max 3 active proposals, 50 recent proposals cap
  • create_proposal validates description length (500 char limit) and rejects empty descriptions
  • Vote mirror API checks membership before recording — only pool members can contribute to the mirror
  • useGovernance hook properly debounces realtime refetches (1.5s) to avoid RPC storms
  • governance.test.ts covers quorum math, time formatting, hex encoding, and proposal merging
  • All 5 CI green, mergeable state clean

Please fix item 1. Item 2 is a suggestion — merge at your discretion.

@Sendi0011

Copy link
Copy Markdown
Contributor

Hi @jajafwangshak86-ops — CI is all green (5/5). There are merge conflicts with main now. Please rebase and push so we can merge this. The empty catch block in mirrorVote (useGovernance.ts:955) still needs addressing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

2 participants