Ending a Poll Early
Create the processors for each of your instructions
Closing a poll early lets the creator end voting before the window expires. Unlike the other instruction handlers, this one doesn't create any new accounts. It simply validates that the caller is the poll's creator, that the poll is still active, and then marks it as closed by flipping closed_early to true.
The process_close_poll handler needs access to two accounts:
- The creator's account, which serves as the signer.
- The poll account, which holds the state you need to update.
Implementing the handler
Start by extracting the accounts and verifying that the creator has signed the transaction:
pub fn process_close_poll(
program_id: &Pubkey,
accounts: &[AccountInfo],
) -> ProgramResult {
let acc_iter = &mut accounts.iter();
let creator_info = next_account_info(acc_iter)?; // signer
let poll_info = next_account_info(acc_iter)?; // Poll PDA
if !creator_info.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}Next, load the poll account and validate it:
if poll_info.owner != program_id {
return Err(ProgramError::IncorrectProgramId);
}
let mut poll: Poll = {
let data = poll_info.try_borrow_data()?;
Poll::try_from_slice(&data).map_err(|_| ProgramError::InvalidAccountData)?
};
poll.assert_valid()?;With the poll loaded, run the three checks that must pass before you touch any state:
- Only the creator can close the poll. The
poll_creatorfield you stored at creation time makes this a single comparison. - The poll must not already be closed. Closing an already-closed poll is a no-op at best and confusing to clients at worst.
- The voting window must still be open. It doesn't make sense to "end early" something that has already ended.
// Access control: only the poll creator may close it
if poll.poll_creator != *creator_info.key {
return Err(ProgramError::MissingRequiredSignature);
}
// Idempotency guard
if poll.closed_early {
return Err(PollError::PollAlreadyClosed.into());
}
// Can't close a poll that has already expired
let clock = Clock::get()?;
if clock.slot > poll.end_slot {
return Err(PollError::VotingWindowClosed.into());
}With all checks passing, flip the flag and write the updated struct back to the account:
poll.closed_early = true;
poll.serialize(&mut &mut poll_info.data.borrow_mut()[..])?;
msg!(
"Event: poll_closed_early - Poll={}, Creator={}",
poll_info.key,
creator_info.key
);
Ok(())
}Info
Notice that closing a poll does not delete the poll account or the associated PollOption accounts. The accounts and their data remain on-chain so clients can still read the final vote tallies. If you want to reclaim the rent stored in those accounts, you would need a separate DeletePoll instruction that transfers their lamport balances back to the creator and zeroes out their data.
You have now implemented all three instruction handlers for your voting program. In the next section, you will wire everything together by writing the program's entrypoint.
Last updated on