pallet_governance/
voting.rs

1use polkadot_sdk::frame_support::{dispatch::DispatchResult, ensure};
2
3use crate::{proposal::ProposalStatus, AccountIdOf, Error, Event, Proposals};
4
5/// Casts a vote on behalf of a voter.
6pub fn add_vote<T: crate::Config>(
7    voter: AccountIdOf<T>,
8    proposal_id: u64,
9    agree: bool,
10) -> DispatchResult {
11    let Some(mut proposal) = Proposals::<T>::get(proposal_id) else {
12        return Err(Error::<T>::ProposalNotFound.into());
13    };
14
15    let crate::proposal::ProposalStatus::Open {
16        votes_for,
17        votes_against,
18        ..
19    } = &mut proposal.status
20    else {
21        return Err(Error::<T>::ProposalClosed.into());
22    };
23
24    ensure!(
25        !votes_for.contains(&voter) && !votes_against.contains(&voter),
26        crate::Error::<T>::AlreadyVoted
27    );
28
29    let voter_delegated_stake = pallet_torus0::stake::sum_staked_by::<T>(&voter);
30    let voter_owned_stake = pallet_torus0::stake::sum_staking_to::<T>(&voter);
31
32    ensure!(
33        voter_delegated_stake > 0 || voter_owned_stake > 0,
34        crate::Error::<T>::InsufficientStake
35    );
36
37    if !crate::NotDelegatingVotingPower::<T>::get().contains(&voter) && voter_delegated_stake == 0 {
38        return Err(Error::<T>::VoterIsDelegatingVotingPower.into());
39    }
40
41    if agree {
42        votes_for
43            .try_insert(voter.clone())
44            .map_err(|_| Error::<T>::InternalError)?;
45    } else {
46        votes_against
47            .try_insert(voter.clone())
48            .map_err(|_| Error::<T>::InternalError)?;
49    }
50
51    Proposals::<T>::insert(proposal.id, proposal);
52    crate::Pallet::<T>::deposit_event(Event::<T>::ProposalVoted(proposal_id, voter, agree));
53    Ok(())
54}
55
56/// Removes the casted vote.
57pub fn remove_vote<T: crate::Config>(voter: AccountIdOf<T>, proposal_id: u64) -> DispatchResult {
58    let Ok(mut proposal) = Proposals::<T>::try_get(proposal_id) else {
59        return Err(Error::<T>::ProposalNotFound.into());
60    };
61
62    let ProposalStatus::Open {
63        votes_for,
64        votes_against,
65        ..
66    } = &mut proposal.status
67    else {
68        return Err(Error::<T>::ProposalClosed.into());
69    };
70
71    let removed = votes_for.remove(&voter) || votes_against.remove(&voter);
72
73    // Check if the voter has actually voted on the proposal
74    ensure!(removed, crate::Error::<T>::NotVoted);
75
76    // Update the proposal in storage
77    Proposals::<T>::insert(proposal.id, proposal);
78    crate::Pallet::<T>::deposit_event(Event::<T>::ProposalVoteUnregistered(proposal_id, voter));
79    Ok(())
80}
81
82/// Gives voting power delegation to the delegator's staked agents.
83pub fn enable_delegation<T: crate::Config>(delegator: AccountIdOf<T>) -> DispatchResult {
84    crate::NotDelegatingVotingPower::<T>::mutate(|delegators| {
85        delegators.remove(&delegator);
86    });
87
88    Ok(())
89}
90
91/// Removes voting power delegation to the delegator's staked agents.
92pub fn disable_delegation<T: crate::Config>(delegator: AccountIdOf<T>) -> DispatchResult {
93    crate::NotDelegatingVotingPower::<T>::mutate(|delegators| {
94        delegators
95            .try_insert(delegator.clone())
96            .map(|_| ())
97            .map_err(|_| Error::<T>::InternalError.into())
98    })
99}