Skip to content

Commit

Permalink
Allow voting on (unexpired) Passed and Rejected proposals
Browse files Browse the repository at this point in the history
For vote tally / statistics
  • Loading branch information
maurolacy committed Aug 23, 2022
1 parent 9494ad7 commit 529bfbf
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 14 deletions.
45 changes: 38 additions & 7 deletions contracts/cw3-fixed-multisig/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,11 @@ pub fn execute_vote(

// ensure proposal exists and can be voted on
let mut prop = PROPOSALS.load(deps.storage, proposal_id)?;
if prop.status != Status::Open {
// Allow voting on Passed and Rejected proposals too,
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 {});
}
Expand Down Expand Up @@ -431,6 +433,7 @@ mod tests {
const VOTER3: &str = "voter0003";
const VOTER4: &str = "voter0004";
const VOTER5: &str = "voter0005";
const VOTER6: &str = "voter0006";
const NOWEIGHT_VOTER: &str = "voterxxxx";
const SOMEBODY: &str = "somebody";

Expand All @@ -457,6 +460,7 @@ mod tests {
voter(VOTER3, 3),
voter(VOTER4, 4),
voter(VOTER5, 5),
voter(VOTER6, 1),
voter(NOWEIGHT_VOTER, 0),
];

Expand Down Expand Up @@ -762,10 +766,19 @@ mod tests {
.add_attribute("status", "Passed")
);

// non-Open proposals cannot be voted
// Passed proposals can still be voted (while they are not expired or executed)
let info = mock_info(VOTER5, &[]);
let err = execute(deps.as_mut(), mock_env(), info, yes_vote).unwrap_err();
assert_eq!(err, ContractError::NotOpen {});
let res = execute(deps.as_mut(), mock_env(), info, yes_vote).unwrap();

// Verify
assert_eq!(
res,
Response::new()
.add_attribute("action", "vote")
.add_attribute("sender", VOTER5)
.add_attribute("proposal_id", proposal_id.to_string())
.add_attribute("status", "Passed")
);

// Propose
let info = mock_info(OWNER, &[]);
Expand Down Expand Up @@ -808,7 +821,7 @@ mod tests {
let info = mock_info(VOTER4, &[]);
let res = execute(deps.as_mut(), mock_env(), info, no_vote.clone()).unwrap();

// Verify it is still open as we actually need no votes > 16 - 3
// Verify it is still open as we actually need no votes > 17 - 3
assert_eq!(
res,
Response::new()
Expand All @@ -826,7 +839,7 @@ mod tests {
let info = mock_info(VOTER5, &[]);
let res = execute(deps.as_mut(), mock_env(), info, no_vote.clone()).unwrap();

// Verify it is still open as we actually need no votes > 16 - 3
// Verify it is still open as we actually need no votes > 17 - 3
assert_eq!(
res,
Response::new()
Expand All @@ -841,7 +854,7 @@ mod tests {
let info = mock_info(VOTER2, &[]);
let res = execute(deps.as_mut(), mock_env(), info, no_vote).unwrap();

// Verify it is rejected as, 15 no votes > 16 - 3
// Verify it is rejected as, 15 no votes > 17 - 3
assert_eq!(
res,
Response::new()
Expand All @@ -850,6 +863,24 @@ mod tests {
.add_attribute("proposal_id", proposal_id.to_string())
.add_attribute("status", "Rejected")
);

// Rejected proposals can still be voted (while they are not expired)
let info = mock_info(VOTER6, &[]);
let yes_vote = ExecuteMsg::Vote {
proposal_id,
vote: Vote::Yes,
};
let res = execute(deps.as_mut(), mock_env(), info, yes_vote).unwrap();

// Verify
assert_eq!(
res,
Response::new()
.add_attribute("action", "vote")
.add_attribute("sender", VOTER6)
.add_attribute("proposal_id", proposal_id.to_string())
.add_attribute("status", "Rejected")
);
}

#[test]
Expand Down
44 changes: 37 additions & 7 deletions contracts/cw3-flex-multisig/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,11 @@ pub fn execute_vote(

// ensure proposal exists and can be voted on
let mut prop = PROPOSALS.load(deps.storage, proposal_id)?;
if prop.status != Status::Open {
// Allow voting on Passed and Rejected proposals too,
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 {});
}
Expand Down Expand Up @@ -1029,11 +1031,20 @@ mod tests {
],
);

// non-Open proposals cannot be voted
let err = app
// Passed proposals can still be voted (while they are not expired or executed)
let res = app
.execute_contract(Addr::unchecked(VOTER5), flex_addr.clone(), &yes_vote, &[])
.unwrap_err();
assert_eq!(ContractError::NotOpen {}, err.downcast().unwrap());
.unwrap();
// Verify
assert_eq!(
res.custom_attrs(1),
[
("action", "vote"),
("sender", VOTER5),
("proposal_id", proposal_id.to_string().as_str()),
("status", "Passed")
]
);

// query individual votes
// initial (with 0 weight)
Expand Down Expand Up @@ -1069,7 +1080,7 @@ mod tests {
);

// non-voter
let voter = VOTER5.into();
let voter = SOMEBODY.into();
let vote: VoteResponse = app
.wrap()
.query_wasm_smart(&flex_addr, &QueryMsg::Vote { proposal_id, voter })
Expand All @@ -1096,7 +1107,7 @@ mod tests {

// Powerful voter opposes it, so it rejects
let res = app
.execute_contract(Addr::unchecked(VOTER4), flex_addr, &no_vote, &[])
.execute_contract(Addr::unchecked(VOTER4), flex_addr.clone(), &no_vote, &[])
.unwrap();

assert_eq!(
Expand All @@ -1108,6 +1119,25 @@ mod tests {
("status", "Rejected"),
],
);

// Rejected proposals can still be voted (while they are not expired)
let yes_vote = ExecuteMsg::Vote {
proposal_id,
vote: Vote::Yes,
};
let res = app
.execute_contract(Addr::unchecked(VOTER5), flex_addr, &yes_vote, &[])
.unwrap();

assert_eq!(
res.custom_attrs(1),
[
("action", "vote"),
("sender", VOTER5),
("proposal_id", proposal_id.to_string().as_str()),
("status", "Rejected"),
],
);
}

#[test]
Expand Down

0 comments on commit 529bfbf

Please sign in to comment.