1use crate::{Conviction, Delegations};
21use codec::{Decode, DecodeWithMemTracking, Encode, EncodeLike, Input, MaxEncodedLen, Output};
22use frame_support::{pallet_prelude::Get, BoundedVec};
23use scale_info::TypeInfo;
24use sp_runtime::{
25 traits::{Saturating, Zero},
26 RuntimeDebug,
27};
28
29#[derive(
31 DecodeWithMemTracking, Copy, Clone, Eq, PartialEq, Default, RuntimeDebug, MaxEncodedLen,
32)]
33pub struct Vote {
34 pub aye: bool,
35 pub conviction: Conviction,
36}
37
38impl Encode for Vote {
39 fn encode_to<T: Output + ?Sized>(&self, output: &mut T) {
40 output.push_byte(u8::from(self.conviction) | if self.aye { 0b1000_0000 } else { 0 });
41 }
42}
43
44impl EncodeLike for Vote {}
45
46impl Decode for Vote {
47 fn decode<I: Input>(input: &mut I) -> Result<Self, codec::Error> {
48 let b = input.read_byte()?;
49 Ok(Vote {
50 aye: (b & 0b1000_0000) == 0b1000_0000,
51 conviction: Conviction::try_from(b & 0b0111_1111)
52 .map_err(|_| codec::Error::from("Invalid conviction"))?,
53 })
54 }
55}
56
57impl TypeInfo for Vote {
58 type Identity = Self;
59
60 fn type_info() -> scale_info::Type {
61 scale_info::Type::builder()
62 .path(scale_info::Path::new("Vote", module_path!()))
63 .composite(
64 scale_info::build::Fields::unnamed()
65 .field(|f| f.ty::<u8>().docs(&["Raw vote byte, encodes aye + conviction"])),
66 )
67 }
68}
69
70#[derive(
72 Encode,
73 Decode,
74 DecodeWithMemTracking,
75 Copy,
76 Clone,
77 Eq,
78 PartialEq,
79 RuntimeDebug,
80 TypeInfo,
81 MaxEncodedLen,
82)]
83pub enum AccountVote<Balance> {
84 Standard { vote: Vote, balance: Balance },
86 Split { aye: Balance, nay: Balance },
89 SplitAbstain { aye: Balance, nay: Balance, abstain: Balance },
93}
94
95#[derive(Copy, Clone, Eq, PartialEq, RuntimeDebug)]
97pub enum LockedIf {
98 Status(bool),
102 Always,
104}
105
106impl<Balance: Saturating> AccountVote<Balance> {
107 pub fn locked_if(self, approved: LockedIf) -> Option<(u32, Balance)> {
110 match (self, approved) {
112 (AccountVote::Standard { vote: Vote { conviction: Conviction::None, .. }, .. }, _) =>
114 None,
115
116 (AccountVote::Standard { vote, balance }, LockedIf::Status(is_approved))
118 if vote.aye == is_approved =>
119 Some((vote.conviction.lock_periods(), balance)),
120
121 (AccountVote::Standard { vote, balance }, LockedIf::Always) =>
123 Some((vote.conviction.lock_periods(), balance)),
124
125 _ => None,
127 }
128 }
129
130 pub fn balance(self) -> Balance {
132 match self {
133 AccountVote::Standard { balance, .. } => balance,
134 AccountVote::Split { aye, nay } => aye.saturating_add(nay),
135 AccountVote::SplitAbstain { aye, nay, abstain } =>
136 aye.saturating_add(nay).saturating_add(abstain),
137 }
138 }
139
140 pub fn as_standard(self) -> Option<bool> {
143 match self {
144 AccountVote::Standard { vote, .. } => Some(vote.aye),
145 _ => None,
146 }
147 }
148}
149
150#[derive(
152 Encode,
153 Decode,
154 DecodeWithMemTracking,
155 Default,
156 Copy,
157 Clone,
158 Eq,
159 PartialEq,
160 Ord,
161 PartialOrd,
162 RuntimeDebug,
163 TypeInfo,
164 MaxEncodedLen,
165)]
166pub struct PriorLock<BlockNumber, Balance>(BlockNumber, Balance);
167
168impl<BlockNumber: Ord + Copy + Zero, Balance: Ord + Copy + Zero> PriorLock<BlockNumber, Balance> {
169 pub fn accumulate(&mut self, until: BlockNumber, amount: Balance) {
171 self.0 = self.0.max(until);
172 self.1 = self.1.max(amount);
173 }
174
175 pub fn locked(&self) -> Balance {
176 self.1
177 }
178
179 pub fn rejig(&mut self, now: BlockNumber) {
180 if now >= self.0 {
181 self.0 = Zero::zero();
182 self.1 = Zero::zero();
183 }
184 }
185}
186
187#[derive(
189 Encode,
190 Decode,
191 DecodeWithMemTracking,
192 Clone,
193 Eq,
194 PartialEq,
195 RuntimeDebug,
196 TypeInfo,
197 MaxEncodedLen,
198)]
199pub struct Delegating<Balance, AccountId, BlockNumber> {
200 pub balance: Balance,
202 pub target: AccountId,
204 pub conviction: Conviction,
207 pub delegations: Delegations<Balance>,
209 pub prior: PriorLock<BlockNumber, Balance>,
211}
212
213#[derive(
215 Encode,
216 Decode,
217 DecodeWithMemTracking,
218 Clone,
219 Eq,
220 PartialEq,
221 RuntimeDebug,
222 TypeInfo,
223 MaxEncodedLen,
224)]
225#[scale_info(skip_type_params(MaxVotes))]
226#[codec(mel_bound(Balance: MaxEncodedLen, BlockNumber: MaxEncodedLen, PollIndex: MaxEncodedLen))]
227pub struct Casting<Balance, BlockNumber, PollIndex, MaxVotes>
228where
229 MaxVotes: Get<u32>,
230{
231 pub votes: BoundedVec<(PollIndex, AccountVote<Balance>), MaxVotes>,
233 pub delegations: Delegations<Balance>,
235 pub prior: PriorLock<BlockNumber, Balance>,
237}
238
239#[derive(
241 Encode,
242 Decode,
243 DecodeWithMemTracking,
244 Clone,
245 Eq,
246 PartialEq,
247 RuntimeDebug,
248 TypeInfo,
249 MaxEncodedLen,
250)]
251#[scale_info(skip_type_params(MaxVotes))]
252#[codec(mel_bound(
253 Balance: MaxEncodedLen, AccountId: MaxEncodedLen, BlockNumber: MaxEncodedLen,
254 PollIndex: MaxEncodedLen,
255))]
256pub enum Voting<Balance, AccountId, BlockNumber, PollIndex, MaxVotes>
257where
258 MaxVotes: Get<u32>,
259{
260 Casting(Casting<Balance, BlockNumber, PollIndex, MaxVotes>),
262 Delegating(Delegating<Balance, AccountId, BlockNumber>),
264}
265
266impl<Balance: Default, AccountId, BlockNumber: Zero, PollIndex, MaxVotes> Default
267 for Voting<Balance, AccountId, BlockNumber, PollIndex, MaxVotes>
268where
269 MaxVotes: Get<u32>,
270{
271 fn default() -> Self {
272 Voting::Casting(Casting {
273 votes: Default::default(),
274 delegations: Default::default(),
275 prior: PriorLock(Zero::zero(), Default::default()),
276 })
277 }
278}
279
280impl<Balance, AccountId, BlockNumber, PollIndex, MaxVotes> AsMut<PriorLock<BlockNumber, Balance>>
281 for Voting<Balance, AccountId, BlockNumber, PollIndex, MaxVotes>
282where
283 MaxVotes: Get<u32>,
284{
285 fn as_mut(&mut self) -> &mut PriorLock<BlockNumber, Balance> {
286 match self {
287 Voting::Casting(Casting { prior, .. }) => prior,
288 Voting::Delegating(Delegating { prior, .. }) => prior,
289 }
290 }
291}
292
293impl<
294 Balance: Saturating + Ord + Zero + Copy,
295 BlockNumber: Ord + Copy + Zero,
296 AccountId,
297 PollIndex,
298 MaxVotes,
299 > Voting<Balance, AccountId, BlockNumber, PollIndex, MaxVotes>
300where
301 MaxVotes: Get<u32>,
302{
303 pub fn rejig(&mut self, now: BlockNumber) {
304 AsMut::<PriorLock<BlockNumber, Balance>>::as_mut(self).rejig(now);
305 }
306
307 pub fn locked_balance(&self) -> Balance {
309 match self {
310 Voting::Casting(Casting { votes, prior, .. }) =>
311 votes.iter().map(|i| i.1.balance()).fold(prior.locked(), |a, i| a.max(i)),
312 Voting::Delegating(Delegating { balance, prior, .. }) => *balance.max(&prior.locked()),
313 }
314 }
315
316 pub fn set_common(
317 &mut self,
318 delegations: Delegations<Balance>,
319 prior: PriorLock<BlockNumber, Balance>,
320 ) {
321 let (d, p) = match self {
322 Voting::Casting(Casting { ref mut delegations, ref mut prior, .. }) =>
323 (delegations, prior),
324 Voting::Delegating(Delegating { ref mut delegations, ref mut prior, .. }) =>
325 (delegations, prior),
326 };
327 *d = delegations;
328 *p = prior;
329 }
330}