From 88b972496e7feb2f97d2913d2365a6fa79073048 Mon Sep 17 00:00:00 2001 From: rupesh-kumar-sah Date: Sat, 3 Oct 2026 02:17:07 +0545 Subject: [PATCH] fix(governance): block execution of proposals with zero cast votes - Add quorum validation in `execute_proposal` to panic with 'no votes cast' when winning_votes == 0 - Prevent zero-vote proposals from executing with winning_choice 0 - Add unit test verifying execute_proposal panics on zero-vote proposals Closes #503 --- contracts/learn-token/src/lib.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/contracts/learn-token/src/lib.rs b/contracts/learn-token/src/lib.rs index ed1865a..2855e9c 100644 --- a/contracts/learn-token/src/lib.rs +++ b/contracts/learn-token/src/lib.rs @@ -1877,6 +1877,10 @@ impl LearnToken { } } + if winning_votes == 0 { + panic!("no votes cast"); + } + proposal.executed = true; proposal.winning_choice = winning_choice; storage::set_proposal(&env, proposal_id, &proposal); @@ -3421,6 +3425,34 @@ mod tests { assert_eq!(prop.winning_choice, 1); } + #[test] + #[should_panic(expected = "no votes cast")] + fn test_execute_proposal_zero_votes_panics() { + let env = Env::default(); + let (_admin, lt_contract_id, _) = setup(&env); + let client = LearnTokenClient::new(&env, <_contract_id); + env.mock_all_auths(); + + env.ledger().with_mut(|li| li.sequence_number = 99); + let snapshot_ledger = env.ledger().sequence(); + client.snapshot(&snapshot_ledger); + env.ledger().with_mut(|li| li.sequence_number = 100); + + let start = 1_000u64; + let end = 2_000u64; + let prop_id = client.create_proposal( + &SorobanString::from_str(&env, "Zero Vote Proposal"), + &2, + &start, + &end, + &snapshot_ledger, + ); + + // Advance time past end_time without any votes + env.ledger().with_mut(|li| li.timestamp = 2_500); + client.execute_proposal(&prop_id); + } + // ── Issue #254: storage size tracking ───────────────────────────────── #[test]