pallet_conviction_voting/
types.rs1use codec::{Codec, Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
21use core::{fmt::Debug, marker::PhantomData};
22use frame_support::{
23 traits::VoteTally, CloneNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound,
24};
25use scale_info::TypeInfo;
26use sp_runtime::{
27 traits::{Saturating, Zero},
28 RuntimeDebug,
29};
30
31use super::*;
32use crate::{AccountVote, Conviction, Vote};
33
34#[derive(
36 CloneNoBound,
37 PartialEqNoBound,
38 EqNoBound,
39 RuntimeDebugNoBound,
40 TypeInfo,
41 Encode,
42 Decode,
43 DecodeWithMemTracking,
44 MaxEncodedLen,
45)]
46#[scale_info(skip_type_params(Total))]
47#[codec(mel_bound(Votes: MaxEncodedLen))]
48pub struct Tally<Votes: Clone + PartialEq + Eq + Debug + TypeInfo + Codec, Total> {
49 pub ayes: Votes,
51 pub nays: Votes,
53 pub support: Votes,
55 dummy: PhantomData<Total>,
57}
58
59impl<
60 Votes: Clone + Default + PartialEq + Eq + Debug + Copy + AtLeast32BitUnsigned + TypeInfo + Codec,
61 Total: Get<Votes>,
62 Class,
63 > VoteTally<Votes, Class> for Tally<Votes, Total>
64{
65 fn new(_: Class) -> Self {
66 Self { ayes: Zero::zero(), nays: Zero::zero(), support: Zero::zero(), dummy: PhantomData }
67 }
68
69 fn ayes(&self, _: Class) -> Votes {
70 self.ayes
71 }
72
73 fn support(&self, _: Class) -> Perbill {
74 Perbill::from_rational(self.support, Total::get())
75 }
76
77 fn approval(&self, _: Class) -> Perbill {
78 Perbill::from_rational(self.ayes, self.ayes.saturating_add(self.nays))
79 }
80
81 #[cfg(feature = "runtime-benchmarks")]
82 fn unanimity(_: Class) -> Self {
83 Self { ayes: Total::get(), nays: Zero::zero(), support: Total::get(), dummy: PhantomData }
84 }
85
86 #[cfg(feature = "runtime-benchmarks")]
87 fn rejection(_: Class) -> Self {
88 Self { ayes: Zero::zero(), nays: Total::get(), support: Total::get(), dummy: PhantomData }
89 }
90
91 #[cfg(feature = "runtime-benchmarks")]
92 fn from_requirements(support: Perbill, approval: Perbill, _: Class) -> Self {
93 let support = support.mul_ceil(Total::get());
94 let ayes = approval.mul_ceil(support);
95 Self { ayes, nays: support - ayes, support, dummy: PhantomData }
96 }
97
98 #[cfg(feature = "runtime-benchmarks")]
99 fn setup(_: Class, _: Perbill) {}
100}
101
102impl<
103 Votes: Clone + Default + PartialEq + Eq + Debug + Copy + AtLeast32BitUnsigned + TypeInfo + Codec,
104 Total: Get<Votes>,
105 > Tally<Votes, Total>
106{
107 pub fn from_vote(vote: Vote, balance: Votes) -> Self {
109 let Delegations { votes, capital } = vote.conviction.votes(balance);
110 Self {
111 ayes: if vote.aye { votes } else { Zero::zero() },
112 nays: if vote.aye { Zero::zero() } else { votes },
113 support: capital,
114 dummy: PhantomData,
115 }
116 }
117
118 pub fn from_parts(
119 ayes_with_conviction: Votes,
120 nays_with_conviction: Votes,
121 support: Votes,
122 ) -> Self {
123 Self { ayes: ayes_with_conviction, nays: nays_with_conviction, support, dummy: PhantomData }
124 }
125
126 pub fn add(&mut self, vote: AccountVote<Votes>) -> Option<()> {
128 match vote {
129 AccountVote::Standard { vote, balance } => {
130 let Delegations { votes, capital } = vote.conviction.votes(balance);
131 match vote.aye {
132 true => {
133 self.support = self.support.checked_add(&capital)?;
134 self.ayes = self.ayes.checked_add(&votes)?
135 },
136 false => self.nays = self.nays.checked_add(&votes)?,
137 }
138 },
139 AccountVote::Split { aye, nay } => {
140 let aye = Conviction::None.votes(aye);
141 let nay = Conviction::None.votes(nay);
142 self.support = self.support.checked_add(&aye.capital)?;
143 self.ayes = self.ayes.checked_add(&aye.votes)?;
144 self.nays = self.nays.checked_add(&nay.votes)?;
145 },
146 AccountVote::SplitAbstain { aye, nay, abstain } => {
147 let aye = Conviction::None.votes(aye);
148 let nay = Conviction::None.votes(nay);
149 let abstain = Conviction::None.votes(abstain);
150 self.support =
151 self.support.checked_add(&aye.capital)?.checked_add(&abstain.capital)?;
152 self.ayes = self.ayes.checked_add(&aye.votes)?;
153 self.nays = self.nays.checked_add(&nay.votes)?;
154 },
155 }
156 Some(())
157 }
158
159 pub fn remove(&mut self, vote: AccountVote<Votes>) -> Option<()> {
161 match vote {
162 AccountVote::Standard { vote, balance } => {
163 let Delegations { votes, capital } = vote.conviction.votes(balance);
164 match vote.aye {
165 true => {
166 self.support = self.support.checked_sub(&capital)?;
167 self.ayes = self.ayes.checked_sub(&votes)?
168 },
169 false => self.nays = self.nays.checked_sub(&votes)?,
170 }
171 },
172 AccountVote::Split { aye, nay } => {
173 let aye = Conviction::None.votes(aye);
174 let nay = Conviction::None.votes(nay);
175 self.support = self.support.checked_sub(&aye.capital)?;
176 self.ayes = self.ayes.checked_sub(&aye.votes)?;
177 self.nays = self.nays.checked_sub(&nay.votes)?;
178 },
179 AccountVote::SplitAbstain { aye, nay, abstain } => {
180 let aye = Conviction::None.votes(aye);
181 let nay = Conviction::None.votes(nay);
182 let abstain = Conviction::None.votes(abstain);
183 self.support =
184 self.support.checked_sub(&aye.capital)?.checked_sub(&abstain.capital)?;
185 self.ayes = self.ayes.checked_sub(&aye.votes)?;
186 self.nays = self.nays.checked_sub(&nay.votes)?;
187 },
188 }
189 Some(())
190 }
191
192 pub fn increase(&mut self, approve: bool, delegations: Delegations<Votes>) {
194 match approve {
195 true => {
196 self.support = self.support.saturating_add(delegations.capital);
197 self.ayes = self.ayes.saturating_add(delegations.votes);
198 },
199 false => self.nays = self.nays.saturating_add(delegations.votes),
200 }
201 }
202
203 pub fn reduce(&mut self, approve: bool, delegations: Delegations<Votes>) {
205 match approve {
206 true => {
207 self.support = self.support.saturating_sub(delegations.capital);
208 self.ayes = self.ayes.saturating_sub(delegations.votes);
209 },
210 false => self.nays = self.nays.saturating_sub(delegations.votes),
211 }
212 }
213}
214
215#[derive(
217 Encode,
218 Decode,
219 DecodeWithMemTracking,
220 Default,
221 Copy,
222 Clone,
223 PartialEq,
224 Eq,
225 RuntimeDebug,
226 TypeInfo,
227 MaxEncodedLen,
228)]
229pub struct Delegations<Balance> {
230 pub votes: Balance,
232 pub capital: Balance,
234}
235
236impl<Balance: Saturating> Saturating for Delegations<Balance> {
237 fn saturating_add(self, o: Self) -> Self {
238 Self {
239 votes: self.votes.saturating_add(o.votes),
240 capital: self.capital.saturating_add(o.capital),
241 }
242 }
243
244 fn saturating_sub(self, o: Self) -> Self {
245 Self {
246 votes: self.votes.saturating_sub(o.votes),
247 capital: self.capital.saturating_sub(o.capital),
248 }
249 }
250
251 fn saturating_mul(self, o: Self) -> Self {
252 Self {
253 votes: self.votes.saturating_mul(o.votes),
254 capital: self.capital.saturating_mul(o.capital),
255 }
256 }
257
258 fn saturating_pow(self, exp: usize) -> Self {
259 Self { votes: self.votes.saturating_pow(exp), capital: self.capital.saturating_pow(exp) }
260 }
261}
262
263pub enum UnvoteScope {
266 Any,
268 OnlyExpired,
270}