It closes a motion that has enough Aye or Nay votes. The reason it doesn't auto-close is that it would be extra logic on every single vote: looking up to see if it has been passed or rejected. Anyone can call it.
Its behavior is in the docs: https://paritytech.github.io/substrate/master/pallet_collective/pallet/enum.Call.html#variant.close
Most pertinent to your question:
- May be called by any signed account in order to finish voting and close the proposal.
- If called before the end of the voting period it will only close the vote if it is has enough votes to be approved or disapproved.
EDIT: Answering your follow up question here to allow for more characters.
The voting on the proposal closes after the voting period, but somebody still has to call the close
function. The close
function is broken into two primary paths: one for if it's called before the end of the voting period, and one for if it's called after. Each path then has sub-paths for what to do if the proposal is approved or not.
We can generally break down the close
function into some chunks:
First, some checks/variable setting. Note specifically that both approved
and disapproved
can be false.
pub fn close(...) -> DispatchResultWithPostInfo {
let _ = ensure_signed(origin)?;
let voting = Self::voting(&proposal_hash).ok_or(Error::<T, I>::ProposalMissing)?;
ensure!(voting.index == index, Error::<T, I>::WrongIndex);
let mut no_votes = voting.nays.len() as MemberCount;
let mut yes_votes = voting.ayes.len() as MemberCount;
let seats = Self::members().len() as MemberCount;
let approved = yes_votes >= voting.threshold;
let disapproved = seats.saturating_sub(no_votes) < voting.threshold;
The next segment checks if one of them is already true (before the voting period is closed). Note that so far, the function hasn't even checked if the voting period is open or closed, it's only checked if there are enough votes to approve or disapprove the proposal.
// Allow (dis-)approving the proposal as soon as there are enough votes.
if approved {
/* snip */
let (proposal_weight, proposal_count) =
Self::do_approve_proposal(seats, yes_votes, proposal_hash, proposal);
return Ok(/* snip */)
} else if disapproved {
Self::deposit_event(Event::Closed { proposal_hash, yes: yes_votes, no: no_votes });
let proposal_count = Self::do_disapprove_proposal(proposal_hash);
return Ok(/* snip */)
}
Assuming it has not been approved or disapproved, then it checks if the voting period is over:
// Only allow actual closing of the proposal after the voting period has ended.
ensure!(
frame_system::Pallet::<T>::block_number() >= voting.end,
Error::<T, I>::TooEarly
);
And finally, if the voting period has ended then it applies the prime
voter logic and determines whether or not it's been approved:
let prime_vote = Self::prime().map(|who| voting.ayes.iter().any(|a| a == &who));
// default voting strategy.
let default = T::DefaultVote::default_vote(prime_vote, yes_votes, no_votes, seats);
let abstentions = seats - (yes_votes + no_votes);
match default {
true => yes_votes += abstentions,
false => no_votes += abstentions,
}
let approved = yes_votes >= voting.threshold;
if approved {
let (proposal, len) = Self::validate_and_get_proposal(
&proposal_hash,
length_bound,
proposal_weight_bound,
)?;
Self::deposit_event(Event::Closed { proposal_hash, yes: yes_votes, no: no_votes });
let (proposal_weight, proposal_count) =
Self::do_approve_proposal(seats, yes_votes, proposal_hash, proposal);
Ok(/* snip */)
} else {
Self::deposit_event(Event::Closed { proposal_hash, yes: yes_votes, no: no_votes });
let proposal_count = Self::do_disapprove_proposal(proposal_hash);
Ok(/* snip */)
}
}