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