diff --git a/packages/voting-contract/src/lib.rs b/packages/voting-contract/src/lib.rs index 29cb211d..c81d6a1f 100644 --- a/packages/voting-contract/src/lib.rs +++ b/packages/voting-contract/src/lib.rs @@ -118,10 +118,15 @@ where // ensure proposal exists and can be voted on let mut prop = proposals().load(deps.storage, proposal_id)?; - if prop.current_status(&env.block) != Status::Open { + if ![Status::Open, Status::Passed, Status::Rejected].contains(&prop.status) { return Err(ContractError::NotOpen {}); } + // if they are not expired + if prop.expires.is_expired(&env.block) { + return Err(ContractError::Expired {}); + } + // use a snapshot of "start of proposal" // Must be a member of voting group and have voting power >= 1 let cfg = CONFIG.load(deps.storage)?; @@ -157,7 +162,8 @@ where P: Serialize + DeserializeOwned, { let mut proposal = proposals::
().load(storage, proposal_id)?; - + // Update Status + proposal.update_status(&env.block); // We allow execution even after the proposal "expiration" as long as all votes come in before // that point. If it was approved on time, it can be executed any time. if proposal.current_status(&env.block) != Status::Passed { diff --git a/packages/voting-contract/src/multitest/voting.rs b/packages/voting-contract/src/multitest/voting.rs index 2cf38b37..e1998c57 100644 --- a/packages/voting-contract/src/multitest/voting.rs +++ b/packages/voting-contract/src/multitest/voting.rs @@ -368,5 +368,50 @@ fn expired_proposals_cannot_be_voted_on() { // Bob can't vote on the expired proposal let err = suite.vote("bob", proposal_id, Vote::Yes).unwrap_err(); // proposal that is open and expired is rejected - assert_eq!(ContractError::NotOpen {}, err.downcast().unwrap()); + assert_eq!(ContractError::Expired {}, err.downcast().unwrap()); +} + +#[test] +fn proposal_pass_on_expiration() { + let rules = RulesBuilder::new() + .with_threshold(Decimal::percent(51)) + .with_quorum(Decimal::percent(35)) + .build(); + + let mut suite = SuiteBuilder::new() + .with_member("alice", 1) + .with_member("bob", 2) + .with_rules(rules.clone()) + .build(); + + // Create proposal with 1 voting power + let response = suite.propose("alice", "cool proposal", "proposal").unwrap(); + let proposal_id: u64 = get_proposal_id(&response).unwrap(); + + // Bob can vote on the proposal + suite.vote("bob", proposal_id, Vote::Yes).unwrap(); + + // Move time forward so voting ends + suite.app.advance_seconds(rules.voting_period_secs()); + + // Verify proposal is now passed + let prop = suite.query_proposal(proposal_id).unwrap(); + assert_eq!(prop.status, Status::Passed); + + // Alice can't vote on the proposal + let err = suite.vote("alice", proposal_id, Vote::Yes).unwrap_err(); + + // cannot vote on proposal as it has expired + assert_eq!(ContractError::Expired {}, err.downcast().unwrap()); + + // But she can execute the proposal + suite.execute_proposal("alice", proposal_id).unwrap(); + + // Verify proposal is now 'executed' + let prop = suite.query_proposal(proposal_id).unwrap(); + assert_eq!(prop.status, Status::Executed); + + // Closing should NOT be possible + let err = suite.close("bob", proposal_id).unwrap_err(); + assert_eq!(ContractError::WrongCloseStatus {}, err.downcast().unwrap()); }