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 Debug,
27};
28
29#[derive(DecodeWithMemTracking, Copy, Clone, Eq, PartialEq, Default, Debug, MaxEncodedLen)]
31pub struct Vote {
32 pub aye: bool,
33 pub conviction: Conviction,
34}
35
36impl Encode for Vote {
37 fn encode_to<T: Output + ?Sized>(&self, output: &mut T) {
38 output.push_byte(u8::from(self.conviction) | if self.aye { 0b1000_0000 } else { 0 });
39 }
40}
41
42impl EncodeLike for Vote {}
43
44impl Decode for Vote {
45 fn decode<I: Input>(input: &mut I) -> Result<Self, codec::Error> {
46 let b = input.read_byte()?;
47 Ok(Vote {
48 aye: (b & 0b1000_0000) == 0b1000_0000,
49 conviction: Conviction::try_from(b & 0b0111_1111)
50 .map_err(|_| codec::Error::from("Invalid conviction"))?,
51 })
52 }
53}
54
55impl TypeInfo for Vote {
56 type Identity = Self;
57
58 fn type_info() -> scale_info::Type {
59 scale_info::Type::builder()
60 .path(scale_info::Path::new("Vote", module_path!()))
61 .composite(
62 scale_info::build::Fields::unnamed()
63 .field(|f| f.ty::<u8>().docs(&["Raw vote byte, encodes aye + conviction"])),
64 )
65 }
66}
67
68#[derive(
70 Encode,
71 Decode,
72 DecodeWithMemTracking,
73 Copy,
74 Clone,
75 Eq,
76 PartialEq,
77 Debug,
78 TypeInfo,
79 MaxEncodedLen,
80)]
81pub enum AccountVote<Balance> {
82 Standard { vote: Vote, balance: Balance },
84 Split { aye: Balance, nay: Balance },
87 SplitAbstain { aye: Balance, nay: Balance, abstain: Balance },
91}
92
93#[derive(Copy, Clone, Eq, PartialEq, Debug)]
95pub enum LockedIf {
96 Status(bool),
100 Always,
102}
103
104impl<Balance: Saturating> AccountVote<Balance> {
105 pub fn locked_if(self, approved: LockedIf) -> Option<(u32, Balance)> {
108 match (self, approved) {
110 (AccountVote::Standard { vote: Vote { conviction: Conviction::None, .. }, .. }, _) =>
112 None,
113
114 (AccountVote::Standard { vote, balance }, LockedIf::Status(is_approved))
116 if vote.aye == is_approved =>
117 Some((vote.conviction.lock_periods(), balance)),
118
119 (AccountVote::Standard { vote, balance }, LockedIf::Always) =>
121 Some((vote.conviction.lock_periods(), balance)),
122
123 _ => None,
125 }
126 }
127
128 pub fn balance(self) -> Balance {
130 match self {
131 AccountVote::Standard { balance, .. } => balance,
132 AccountVote::Split { aye, nay } => aye.saturating_add(nay),
133 AccountVote::SplitAbstain { aye, nay, abstain } =>
134 aye.saturating_add(nay).saturating_add(abstain),
135 }
136 }
137
138 pub fn as_standard(self) -> Option<bool> {
141 match self {
142 AccountVote::Standard { vote, .. } => Some(vote.aye),
143 _ => None,
144 }
145 }
146}
147
148#[derive(
150 Encode,
151 Decode,
152 DecodeWithMemTracking,
153 Default,
154 Copy,
155 Clone,
156 Eq,
157 PartialEq,
158 Ord,
159 PartialOrd,
160 Debug,
161 TypeInfo,
162 MaxEncodedLen,
163)]
164pub struct PriorLock<BlockNumber, Balance>(BlockNumber, Balance);
165
166impl<BlockNumber: Ord + Copy + Zero, Balance: Ord + Copy + Zero> PriorLock<BlockNumber, Balance> {
167 pub fn accumulate(&mut self, until: BlockNumber, amount: Balance) {
169 self.0 = self.0.max(until);
170 self.1 = self.1.max(amount);
171 }
172
173 pub fn locked(&self) -> Balance {
174 self.1
175 }
176
177 pub fn rejig(&mut self, now: BlockNumber) {
178 if now >= self.0 {
179 self.0 = Zero::zero();
180 self.1 = Zero::zero();
181 }
182 }
183}
184
185#[derive(
187 Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, Debug, TypeInfo, MaxEncodedLen,
188)]
189pub struct Delegating<Balance, AccountId, BlockNumber> {
190 pub balance: Balance,
192 pub target: AccountId,
194 pub conviction: Conviction,
197 pub delegations: Delegations<Balance>,
199 pub prior: PriorLock<BlockNumber, Balance>,
201}
202
203#[derive(
205 Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, Debug, TypeInfo, MaxEncodedLen,
206)]
207#[scale_info(skip_type_params(MaxVotes))]
208#[codec(mel_bound(Balance: MaxEncodedLen, BlockNumber: MaxEncodedLen, PollIndex: MaxEncodedLen))]
209pub struct Casting<Balance, BlockNumber, PollIndex, MaxVotes>
210where
211 MaxVotes: Get<u32>,
212{
213 pub votes: BoundedVec<(PollIndex, AccountVote<Balance>), MaxVotes>,
215 pub delegations: Delegations<Balance>,
217 pub prior: PriorLock<BlockNumber, Balance>,
219}
220
221#[derive(
223 Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, Debug, TypeInfo, MaxEncodedLen,
224)]
225#[scale_info(skip_type_params(MaxVotes))]
226#[codec(mel_bound(
227 Balance: MaxEncodedLen, AccountId: MaxEncodedLen, BlockNumber: MaxEncodedLen,
228 PollIndex: MaxEncodedLen,
229))]
230pub enum Voting<Balance, AccountId, BlockNumber, PollIndex, MaxVotes>
231where
232 MaxVotes: Get<u32>,
233{
234 Casting(Casting<Balance, BlockNumber, PollIndex, MaxVotes>),
236 Delegating(Delegating<Balance, AccountId, BlockNumber>),
238}
239
240impl<Balance: Default, AccountId, BlockNumber: Zero, PollIndex, MaxVotes> Default
241 for Voting<Balance, AccountId, BlockNumber, PollIndex, MaxVotes>
242where
243 MaxVotes: Get<u32>,
244{
245 fn default() -> Self {
246 Voting::Casting(Casting {
247 votes: Default::default(),
248 delegations: Default::default(),
249 prior: PriorLock(Zero::zero(), Default::default()),
250 })
251 }
252}
253
254impl<Balance, AccountId, BlockNumber, PollIndex, MaxVotes> AsMut<PriorLock<BlockNumber, Balance>>
255 for Voting<Balance, AccountId, BlockNumber, PollIndex, MaxVotes>
256where
257 MaxVotes: Get<u32>,
258{
259 fn as_mut(&mut self) -> &mut PriorLock<BlockNumber, Balance> {
260 match self {
261 Voting::Casting(Casting { prior, .. }) => prior,
262 Voting::Delegating(Delegating { prior, .. }) => prior,
263 }
264 }
265}
266
267impl<
268 Balance: Saturating + Ord + Zero + Copy,
269 BlockNumber: Ord + Copy + Zero,
270 AccountId,
271 PollIndex,
272 MaxVotes,
273 > Voting<Balance, AccountId, BlockNumber, PollIndex, MaxVotes>
274where
275 MaxVotes: Get<u32>,
276{
277 pub fn rejig(&mut self, now: BlockNumber) {
278 AsMut::<PriorLock<BlockNumber, Balance>>::as_mut(self).rejig(now);
279 }
280
281 pub fn locked_balance(&self) -> Balance {
283 match self {
284 Voting::Casting(Casting { votes, prior, .. }) =>
285 votes.iter().map(|i| i.1.balance()).fold(prior.locked(), |a, i| a.max(i)),
286 Voting::Delegating(Delegating { balance, prior, .. }) => *balance.max(&prior.locked()),
287 }
288 }
289
290 pub fn set_common(
291 &mut self,
292 delegations: Delegations<Balance>,
293 prior: PriorLock<BlockNumber, Balance>,
294 ) {
295 let (d, p) = match self {
296 Voting::Casting(Casting { ref mut delegations, ref mut prior, .. }) =>
297 (delegations, prior),
298 Voting::Delegating(Delegating { ref mut delegations, ref mut prior, .. }) =>
299 (delegations, prior),
300 };
301 *d = delegations;
302 *p = prior;
303 }
304}