1use crate::{Conviction, Delegations, ReferendumIndex};
21use codec::{Decode, DecodeWithMemTracking, Encode, EncodeLike, Input, MaxEncodedLen, Output};
22use frame_support::traits::Get;
23use scale_info::TypeInfo;
24use sp_runtime::{
25 traits::{Saturating, Zero},
26 BoundedVec, Debug,
27};
28
29#[derive(DecodeWithMemTracking, Copy, Clone, Eq, PartialEq, Default, Debug)]
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 MaxEncodedLen for Vote {
43 fn max_encoded_len() -> usize {
44 1
45 }
46}
47
48impl EncodeLike for Vote {}
49
50impl Decode for Vote {
51 fn decode<I: Input>(input: &mut I) -> Result<Self, codec::Error> {
52 let b = input.read_byte()?;
53 Ok(Vote {
54 aye: (b & 0b1000_0000) == 0b1000_0000,
55 conviction: Conviction::try_from(b & 0b0111_1111)
56 .map_err(|_| codec::Error::from("Invalid conviction"))?,
57 })
58 }
59}
60
61impl TypeInfo for Vote {
62 type Identity = Self;
63
64 fn type_info() -> scale_info::Type {
65 scale_info::Type::builder()
66 .path(scale_info::Path::new("Vote", module_path!()))
67 .composite(
68 scale_info::build::Fields::unnamed()
69 .field(|f| f.ty::<u8>().docs(&["Raw vote byte, encodes aye + conviction"])),
70 )
71 }
72}
73
74#[derive(
76 Encode,
77 DecodeWithMemTracking,
78 MaxEncodedLen,
79 Decode,
80 Copy,
81 Clone,
82 Eq,
83 PartialEq,
84 Debug,
85 TypeInfo,
86)]
87pub enum AccountVote<Balance> {
88 Standard { vote: Vote, balance: Balance },
90 Split { aye: Balance, nay: Balance },
93}
94
95impl<Balance: Saturating> AccountVote<Balance> {
96 pub fn locked_if(self, approved: bool) -> Option<(u32, Balance)> {
99 match self {
101 AccountVote::Standard { vote, balance } if vote.aye == approved => {
102 Some((vote.conviction.lock_periods(), balance))
103 },
104 _ => None,
105 }
106 }
107
108 pub fn balance(self) -> Balance {
110 match self {
111 AccountVote::Standard { balance, .. } => balance,
112 AccountVote::Split { aye, nay } => aye.saturating_add(nay),
113 }
114 }
115
116 pub fn as_standard(self) -> Option<bool> {
119 match self {
120 AccountVote::Standard { vote, .. } => Some(vote.aye),
121 _ => None,
122 }
123 }
124}
125
126#[derive(
128 Encode,
129 MaxEncodedLen,
130 Decode,
131 Default,
132 Copy,
133 Clone,
134 Eq,
135 PartialEq,
136 Ord,
137 PartialOrd,
138 Debug,
139 TypeInfo,
140)]
141pub struct PriorLock<BlockNumber, Balance>(BlockNumber, Balance);
142
143impl<BlockNumber: Ord + Copy + Zero, Balance: Ord + Copy + Zero> PriorLock<BlockNumber, Balance> {
144 pub fn accumulate(&mut self, until: BlockNumber, amount: Balance) {
146 self.0 = self.0.max(until);
147 self.1 = self.1.max(amount);
148 }
149
150 pub fn locked(&self) -> Balance {
151 self.1
152 }
153
154 pub fn rejig(&mut self, now: BlockNumber) {
155 if now >= self.0 {
156 self.0 = Zero::zero();
157 self.1 = Zero::zero();
158 }
159 }
160}
161
162#[derive(Clone, Encode, Decode, Eq, MaxEncodedLen, PartialEq, Debug, TypeInfo)]
164#[codec(mel_bound(skip_type_params(MaxVotes)))]
165#[scale_info(skip_type_params(MaxVotes))]
166pub enum Voting<Balance, AccountId, BlockNumber, MaxVotes: Get<u32>> {
167 Direct {
170 votes: BoundedVec<(ReferendumIndex, AccountVote<Balance>), MaxVotes>,
172 delegations: Delegations<Balance>,
174 prior: PriorLock<BlockNumber, Balance>,
176 },
177 Delegating {
179 balance: Balance,
180 target: AccountId,
181 conviction: Conviction,
182 delegations: Delegations<Balance>,
184 prior: PriorLock<BlockNumber, Balance>,
186 },
187}
188
189impl<Balance: Default, AccountId, BlockNumber: Zero, MaxVotes: Get<u32>> Default
190 for Voting<Balance, AccountId, BlockNumber, MaxVotes>
191{
192 fn default() -> Self {
193 Voting::Direct {
194 votes: Default::default(),
195 delegations: Default::default(),
196 prior: PriorLock(Zero::zero(), Default::default()),
197 }
198 }
199}
200
201impl<
202 Balance: Saturating + Ord + Zero + Copy,
203 BlockNumber: Ord + Copy + Zero,
204 AccountId,
205 MaxVotes: Get<u32>,
206 > Voting<Balance, AccountId, BlockNumber, MaxVotes>
207{
208 pub fn rejig(&mut self, now: BlockNumber) {
209 match self {
210 Voting::Direct { prior, .. } => prior,
211 Voting::Delegating { prior, .. } => prior,
212 }
213 .rejig(now);
214 }
215
216 pub fn locked_balance(&self) -> Balance {
218 match self {
219 Voting::Direct { votes, prior, .. } => {
220 votes.iter().map(|i| i.1.balance()).fold(prior.locked(), |a, i| a.max(i))
221 },
222 Voting::Delegating { balance, prior, .. } => *balance.max(&prior.locked()),
223 }
224 }
225
226 pub fn set_common(
227 &mut self,
228 delegations: Delegations<Balance>,
229 prior: PriorLock<BlockNumber, Balance>,
230 ) {
231 let (d, p) = match self {
232 Voting::Direct { ref mut delegations, ref mut prior, .. } => (delegations, prior),
233 Voting::Delegating { ref mut delegations, ref mut prior, .. } => (delegations, prior),
234 };
235 *d = delegations;
236 *p = prior;
237 }
238
239 pub fn prior(&self) -> &PriorLock<BlockNumber, Balance> {
240 match self {
241 Voting::Direct { prior, .. } => prior,
242 Voting::Delegating { prior, .. } => prior,
243 }
244 }
245}