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 let total = self.ayes.saturating_add(self.nays);
74 if total.is_zero() {
75 Perbill::zero()
76 } else {
77 Perbill::from_rational(self.ayes, total)
78 }
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<()> {
130 let (mut ayes, mut nays, mut support) = (self.ayes, self.nays, self.support);
131 match vote {
132 AccountVote::Standard { vote, balance } => {
133 let Delegations { votes, capital } = vote.conviction.votes(balance);
134 match vote.aye {
135 true => {
136 support = support.checked_add(&capital)?;
137 ayes = ayes.checked_add(&votes)?
138 },
139 false => nays = nays.checked_add(&votes)?,
140 }
141 },
142 AccountVote::Split { aye, nay } => {
143 let aye = Conviction::None.votes(aye);
144 let nay = Conviction::None.votes(nay);
145 support = support.checked_add(&aye.capital)?;
146 ayes = ayes.checked_add(&aye.votes)?;
147 nays = nays.checked_add(&nay.votes)?;
148 },
149 AccountVote::SplitAbstain { aye, nay, abstain } => {
150 let aye = Conviction::None.votes(aye);
151 let nay = Conviction::None.votes(nay);
152 let abstain = Conviction::None.votes(abstain);
153 support = support.checked_add(&aye.capital)?.checked_add(&abstain.capital)?;
154 ayes = ayes.checked_add(&aye.votes)?;
155 nays = nays.checked_add(&nay.votes)?;
156 },
157 }
158 *self = Self::from_parts(ayes, nays, support);
160 Some(())
161 }
162
163 pub fn remove(&mut self, vote: AccountVote<Votes>) -> Option<()> {
167 let (mut ayes, mut nays, mut support) = (self.ayes, self.nays, self.support);
168 match vote {
169 AccountVote::Standard { vote, balance } => {
170 let Delegations { votes, capital } = vote.conviction.votes(balance);
171 match vote.aye {
172 true => {
173 support = support.checked_sub(&capital)?;
174 ayes = ayes.checked_sub(&votes)?
175 },
176 false => nays = nays.checked_sub(&votes)?,
177 }
178 },
179 AccountVote::Split { aye, nay } => {
180 let aye = Conviction::None.votes(aye);
181 let nay = Conviction::None.votes(nay);
182 support = support.checked_sub(&aye.capital)?;
183 ayes = ayes.checked_sub(&aye.votes)?;
184 nays = nays.checked_sub(&nay.votes)?;
185 },
186 AccountVote::SplitAbstain { aye, nay, abstain } => {
187 let aye = Conviction::None.votes(aye);
188 let nay = Conviction::None.votes(nay);
189 let abstain = Conviction::None.votes(abstain);
190 support = support.checked_sub(&aye.capital)?.checked_sub(&abstain.capital)?;
191 ayes = ayes.checked_sub(&aye.votes)?;
192 nays = nays.checked_sub(&nay.votes)?;
193 },
194 }
195 *self = Self::from_parts(ayes, nays, support);
197 Some(())
198 }
199
200 pub fn increase(&mut self, approve: bool, delegations: Delegations<Votes>) {
202 match approve {
203 true => {
204 self.support = self.support.saturating_add(delegations.capital);
205 self.ayes = self.ayes.saturating_add(delegations.votes);
206 },
207 false => self.nays = self.nays.saturating_add(delegations.votes),
208 }
209 }
210
211 pub fn reduce(&mut self, approve: bool, delegations: Delegations<Votes>) {
213 match approve {
214 true => {
215 self.support = self.support.saturating_sub(delegations.capital);
216 self.ayes = self.ayes.saturating_sub(delegations.votes);
217 },
218 false => self.nays = self.nays.saturating_sub(delegations.votes),
219 }
220 }
221}
222
223#[derive(
225 Encode,
226 Decode,
227 DecodeWithMemTracking,
228 Default,
229 Copy,
230 Clone,
231 PartialEq,
232 Eq,
233 Debug,
234 TypeInfo,
235 MaxEncodedLen,
236)]
237pub struct Delegations<Balance> {
238 pub votes: Balance,
240 pub capital: Balance,
242}
243
244impl<Balance: Saturating> Saturating for Delegations<Balance> {
245 fn saturating_add(self, o: Self) -> Self {
246 Self {
247 votes: self.votes.saturating_add(o.votes),
248 capital: self.capital.saturating_add(o.capital),
249 }
250 }
251
252 fn saturating_sub(self, o: Self) -> Self {
253 Self {
254 votes: self.votes.saturating_sub(o.votes),
255 capital: self.capital.saturating_sub(o.capital),
256 }
257 }
258
259 fn saturating_mul(self, o: Self) -> Self {
260 Self {
261 votes: self.votes.saturating_mul(o.votes),
262 capital: self.capital.saturating_mul(o.capital),
263 }
264 }
265
266 fn saturating_pow(self, exp: usize) -> Self {
267 Self { votes: self.votes.saturating_pow(exp), capital: self.capital.saturating_pow(exp) }
268 }
269}
270
271pub enum UnvoteScope {
274 Any,
276 OnlyExpired,
278}