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, RuntimeDebug,
27};
28
29#[derive(DecodeWithMemTracking, Copy, Clone, Eq, PartialEq, Default, RuntimeDebug)]
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 RuntimeDebug,
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 _ => None,
104 }
105 }
106
107 pub fn balance(self) -> Balance {
109 match self {
110 AccountVote::Standard { balance, .. } => balance,
111 AccountVote::Split { aye, nay } => aye.saturating_add(nay),
112 }
113 }
114
115 pub fn as_standard(self) -> Option<bool> {
118 match self {
119 AccountVote::Standard { vote, .. } => Some(vote.aye),
120 _ => None,
121 }
122 }
123}
124
125#[derive(
127 Encode,
128 MaxEncodedLen,
129 Decode,
130 Default,
131 Copy,
132 Clone,
133 Eq,
134 PartialEq,
135 Ord,
136 PartialOrd,
137 RuntimeDebug,
138 TypeInfo,
139)]
140pub struct PriorLock<BlockNumber, Balance>(BlockNumber, Balance);
141
142impl<BlockNumber: Ord + Copy + Zero, Balance: Ord + Copy + Zero> PriorLock<BlockNumber, Balance> {
143 pub fn accumulate(&mut self, until: BlockNumber, amount: Balance) {
145 self.0 = self.0.max(until);
146 self.1 = self.1.max(amount);
147 }
148
149 pub fn locked(&self) -> Balance {
150 self.1
151 }
152
153 pub fn rejig(&mut self, now: BlockNumber) {
154 if now >= self.0 {
155 self.0 = Zero::zero();
156 self.1 = Zero::zero();
157 }
158 }
159}
160
161#[derive(Clone, Encode, Decode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)]
163#[codec(mel_bound(skip_type_params(MaxVotes)))]
164#[scale_info(skip_type_params(MaxVotes))]
165pub enum Voting<Balance, AccountId, BlockNumber, MaxVotes: Get<u32>> {
166 Direct {
169 votes: BoundedVec<(ReferendumIndex, AccountVote<Balance>), MaxVotes>,
171 delegations: Delegations<Balance>,
173 prior: PriorLock<BlockNumber, Balance>,
175 },
176 Delegating {
178 balance: Balance,
179 target: AccountId,
180 conviction: Conviction,
181 delegations: Delegations<Balance>,
183 prior: PriorLock<BlockNumber, Balance>,
185 },
186}
187
188impl<Balance: Default, AccountId, BlockNumber: Zero, MaxVotes: Get<u32>> Default
189 for Voting<Balance, AccountId, BlockNumber, MaxVotes>
190{
191 fn default() -> Self {
192 Voting::Direct {
193 votes: Default::default(),
194 delegations: Default::default(),
195 prior: PriorLock(Zero::zero(), Default::default()),
196 }
197 }
198}
199
200impl<
201 Balance: Saturating + Ord + Zero + Copy,
202 BlockNumber: Ord + Copy + Zero,
203 AccountId,
204 MaxVotes: Get<u32>,
205 > Voting<Balance, AccountId, BlockNumber, MaxVotes>
206{
207 pub fn rejig(&mut self, now: BlockNumber) {
208 match self {
209 Voting::Direct { prior, .. } => prior,
210 Voting::Delegating { prior, .. } => prior,
211 }
212 .rejig(now);
213 }
214
215 pub fn locked_balance(&self) -> Balance {
217 match self {
218 Voting::Direct { votes, prior, .. } =>
219 votes.iter().map(|i| i.1.balance()).fold(prior.locked(), |a, i| a.max(i)),
220 Voting::Delegating { balance, prior, .. } => *balance.max(&prior.locked()),
221 }
222 }
223
224 pub fn set_common(
225 &mut self,
226 delegations: Delegations<Balance>,
227 prior: PriorLock<BlockNumber, Balance>,
228 ) {
229 let (d, p) = match self {
230 Voting::Direct { ref mut delegations, ref mut prior, .. } => (delegations, prior),
231 Voting::Delegating { ref mut delegations, ref mut prior, .. } => (delegations, prior),
232 };
233 *d = delegations;
234 *p = prior;
235 }
236
237 pub fn prior(&self) -> &PriorLock<BlockNumber, Balance> {
238 match self {
239 Voting::Direct { prior, .. } => prior,
240 Voting::Delegating { prior, .. } => prior,
241 }
242 }
243}