feat(governance): DAO-style governance voting for pool parameters - #249
feat(governance): DAO-style governance voting for pool parameters#249jajafwangshak86-ops wants to merge 1 commit into
Conversation
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
left a comment
There was a problem hiding this comment.
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:955 — mirrorVote 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_proposalvalidates description length (500 char limit) and rejects empty descriptions- Vote mirror API checks membership before recording — only pool members can contribute to the mirror
useGovernancehook properly debounces realtime refetches (1.5s) to avoid RPC stormsgovernance.test.tscovers 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.
|
Hi @jajafwangshak86-ops — CI is all green (5/5). There are merge conflicts with |
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 capvote(voter, id, in_favor)— one vote per member per proposal, proposers cannot vote; enough against-votes auto-rejectsexecute_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_quorumPool 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), coveringdeposit_amount,round_duration/deadline, andpenalty_percentagegovernance_contract(),penalty_percentage(), plusminimum_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 proposalshooks/useGovernance.ts— proposals/quorum/member-count fetching +createProposal/vote/executeProposalmutations + Supabase Realtime subscription on the votes mirror for live counts without refreshuseJointSaveContracts.ts(proposal ScVal parsing + tx builders)lib/governance.tspure helpers with 8 unit tests wired intotest:unit; en/es translationsDatabase
supabase/migrations/20260824000000_governance_votes.sql: off-chaingovernance_votesmirror (unique per proposal+voter, RLS, realtime publication) andpools.governance_contract_idwhich gates the Governance section in the group pagePOST /api/governancevalidates membership before writing the mirrorVerification
cargo test: 188 tests pass across the workspace; clippy clean for the new contractpnpm lint,pnpm format:check,pnpm build,check-budget,test:unit(210),test:components(101) all green locally