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
115 (AccountVote::Standard { vote, balance }, LockedIf::Status(is_approved))
117 if vote.aye == is_approved =>
118 {
119 Some((vote.conviction.lock_periods(), balance))
120 },
121
122 (AccountVote::Standard { vote, balance }, LockedIf::Always) => {
124 Some((vote.conviction.lock_periods(), balance))
125 },
126
127 _ => None,
129 }
130 }
131
132 pub fn balance(self) -> Balance {
134 match self {
135 AccountVote::Standard { balance, .. } => balance,
136 AccountVote::Split { aye, nay } => aye.saturating_add(nay),
137 AccountVote::SplitAbstain { aye, nay, abstain } => {
138 aye.saturating_add(nay).saturating_add(abstain)
139 },
140 }
141 }
142
143 pub fn as_standard(self) -> Option<bool> {
146 match self {
147 AccountVote::Standard { vote, .. } => Some(vote.aye),
148 _ => None,
149 }
150 }
151}
152
153#[derive(
155 Encode,
156 Decode,
157 DecodeWithMemTracking,
158 Default,
159 Copy,
160 Clone,
161 Eq,
162 PartialEq,
163 Ord,
164 PartialOrd,
165 Debug,
166 TypeInfo,
167 MaxEncodedLen,
168)]
169pub struct PriorLock<BlockNumber, Balance>(BlockNumber, Balance);
170
171impl<BlockNumber: Ord + Copy + Zero, Balance: Ord + Copy + Zero> PriorLock<BlockNumber, Balance> {
172 pub fn accumulate(&mut self, until: BlockNumber, amount: Balance) {
174 self.0 = self.0.max(until);
175 self.1 = self.1.max(amount);
176 }
177
178 pub fn locked(&self) -> Balance {
179 self.1
180 }
181
182 pub fn rejig(&mut self, now: BlockNumber) {
183 if now >= self.0 {
184 self.0 = Zero::zero();
185 self.1 = Zero::zero();
186 }
187 }
188}
189
190#[derive(
192 Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, Debug, TypeInfo, MaxEncodedLen,
193)]
194pub struct Delegating<Balance, AccountId, BlockNumber> {
195 pub balance: Balance,
197 pub target: AccountId,
199 pub conviction: Conviction,
202 pub delegations: Delegations<Balance>,
204 pub prior: PriorLock<BlockNumber, Balance>,
206}
207
208#[derive(
210 Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, Debug, TypeInfo, MaxEncodedLen,
211)]
212#[scale_info(skip_type_params(MaxVotes))]
213#[codec(mel_bound(Balance: MaxEncodedLen, BlockNumber: MaxEncodedLen, PollIndex: MaxEncodedLen))]
214pub struct Casting<Balance, BlockNumber, PollIndex, MaxVotes>
215where
216 MaxVotes: Get<u32>,
217{
218 pub votes: BoundedVec<(PollIndex, AccountVote<Balance>), MaxVotes>,
220 pub delegations: Delegations<Balance>,
222 pub prior: PriorLock<BlockNumber, Balance>,
224}
225
226#[derive(
228 Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, Debug, TypeInfo, MaxEncodedLen,
229)]
230#[scale_info(skip_type_params(MaxVotes))]
231#[codec(mel_bound(
232 Balance: MaxEncodedLen, AccountId: MaxEncodedLen, BlockNumber: MaxEncodedLen,
233 PollIndex: MaxEncodedLen,
234))]
235pub enum Voting<Balance, AccountId, BlockNumber, PollIndex, MaxVotes>
236where
237 MaxVotes: Get<u32>,
238{
239 Casting(Casting<Balance, BlockNumber, PollIndex, MaxVotes>),
241 Delegating(Delegating<Balance, AccountId, BlockNumber>),
243}
244
245impl<Balance: Default, AccountId, BlockNumber: Zero, PollIndex, MaxVotes> Default
246 for Voting<Balance, AccountId, BlockNumber, PollIndex, MaxVotes>
247where
248 MaxVotes: Get<u32>,
249{
250 fn default() -> Self {
251 Voting::Casting(Casting {
252 votes: Default::default(),
253 delegations: Default::default(),
254 prior: PriorLock(Zero::zero(), Default::default()),
255 })
256 }
257}
258
259impl<Balance, AccountId, BlockNumber, PollIndex, MaxVotes> AsMut<PriorLock<BlockNumber, Balance>>
260 for Voting<Balance, AccountId, BlockNumber, PollIndex, MaxVotes>
261where
262 MaxVotes: Get<u32>,
263{
264 fn as_mut(&mut self) -> &mut PriorLock<BlockNumber, Balance> {
265 match self {
266 Voting::Casting(Casting { prior, .. }) => prior,
267 Voting::Delegating(Delegating { prior, .. }) => prior,
268 }
269 }
270}
271
272impl<
273 Balance: Saturating + Ord + Zero + Copy,
274 BlockNumber: Ord + Copy + Zero,
275 AccountId,
276 PollIndex,
277 MaxVotes,
278 > Voting<Balance, AccountId, BlockNumber, PollIndex, MaxVotes>
279where
280 MaxVotes: Get<u32>,
281{
282 pub fn rejig(&mut self, now: BlockNumber) {
283 AsMut::<PriorLock<BlockNumber, Balance>>::as_mut(self).rejig(now);
284 }
285
286 pub fn locked_balance(&self) -> Balance {
288 match self {
289 Voting::Casting(Casting { votes, prior, .. }) => {
290 votes.iter().map(|i| i.1.balance()).fold(prior.locked(), |a, i| a.max(i))
291 },
292 Voting::Delegating(Delegating { balance, prior, .. }) => *balance.max(&prior.locked()),
293 }
294 }
295
296 pub fn set_common(
297 &mut self,
298 delegations: Delegations<Balance>,
299 prior: PriorLock<BlockNumber, Balance>,
300 ) {
301 let (d, p) = match self {
302 Voting::Casting(Casting { ref mut delegations, ref mut prior, .. }) => {
303 (delegations, prior)
304 },
305 Voting::Delegating(Delegating { ref mut delegations, ref mut prior, .. }) => {
306 (delegations, prior)
307 },
308 };
309 *d = delegations;
310 *p = prior;
311 }
312}