use crate::{AccountVote, Conviction, Vote, VoteThreshold};
use codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use sp_runtime::{
traits::{Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Saturating, Zero},
RuntimeDebug,
};
#[derive(Encode, MaxEncodedLen, Decode, Default, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
pub struct Tally<Balance> {
pub ayes: Balance,
pub nays: Balance,
pub turnout: Balance,
}
#[derive(
Encode, MaxEncodedLen, Decode, Default, Copy, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo,
)]
pub struct Delegations<Balance> {
pub votes: Balance,
pub capital: Balance,
}
impl<Balance: Saturating> Saturating for Delegations<Balance> {
fn saturating_add(self, o: Self) -> Self {
Self {
votes: self.votes.saturating_add(o.votes),
capital: self.capital.saturating_add(o.capital),
}
}
fn saturating_sub(self, o: Self) -> Self {
Self {
votes: self.votes.saturating_sub(o.votes),
capital: self.capital.saturating_sub(o.capital),
}
}
fn saturating_mul(self, o: Self) -> Self {
Self {
votes: self.votes.saturating_mul(o.votes),
capital: self.capital.saturating_mul(o.capital),
}
}
fn saturating_pow(self, exp: usize) -> Self {
Self { votes: self.votes.saturating_pow(exp), capital: self.capital.saturating_pow(exp) }
}
}
impl<
Balance: From<u8>
+ Zero
+ Copy
+ CheckedAdd
+ CheckedSub
+ CheckedMul
+ CheckedDiv
+ Bounded
+ Saturating,
> Tally<Balance>
{
pub fn new(vote: Vote, balance: Balance) -> Self {
let Delegations { votes, capital } = vote.conviction.votes(balance);
Self {
ayes: if vote.aye { votes } else { Zero::zero() },
nays: if vote.aye { Zero::zero() } else { votes },
turnout: capital,
}
}
pub fn add(&mut self, vote: AccountVote<Balance>) -> Option<()> {
match vote {
AccountVote::Standard { vote, balance } => {
let Delegations { votes, capital } = vote.conviction.votes(balance);
self.turnout = self.turnout.checked_add(&capital)?;
match vote.aye {
true => self.ayes = self.ayes.checked_add(&votes)?,
false => self.nays = self.nays.checked_add(&votes)?,
}
},
AccountVote::Split { aye, nay } => {
let aye = Conviction::None.votes(aye);
let nay = Conviction::None.votes(nay);
self.turnout = self.turnout.checked_add(&aye.capital)?.checked_add(&nay.capital)?;
self.ayes = self.ayes.checked_add(&aye.votes)?;
self.nays = self.nays.checked_add(&nay.votes)?;
},
}
Some(())
}
pub fn remove(&mut self, vote: AccountVote<Balance>) -> Option<()> {
match vote {
AccountVote::Standard { vote, balance } => {
let Delegations { votes, capital } = vote.conviction.votes(balance);
self.turnout = self.turnout.checked_sub(&capital)?;
match vote.aye {
true => self.ayes = self.ayes.checked_sub(&votes)?,
false => self.nays = self.nays.checked_sub(&votes)?,
}
},
AccountVote::Split { aye, nay } => {
let aye = Conviction::None.votes(aye);
let nay = Conviction::None.votes(nay);
self.turnout = self.turnout.checked_sub(&aye.capital)?.checked_sub(&nay.capital)?;
self.ayes = self.ayes.checked_sub(&aye.votes)?;
self.nays = self.nays.checked_sub(&nay.votes)?;
},
}
Some(())
}
pub fn increase(&mut self, approve: bool, delegations: Delegations<Balance>) -> Option<()> {
self.turnout = self.turnout.saturating_add(delegations.capital);
match approve {
true => self.ayes = self.ayes.saturating_add(delegations.votes),
false => self.nays = self.nays.saturating_add(delegations.votes),
}
Some(())
}
pub fn reduce(&mut self, approve: bool, delegations: Delegations<Balance>) -> Option<()> {
self.turnout = self.turnout.saturating_sub(delegations.capital);
match approve {
true => self.ayes = self.ayes.saturating_sub(delegations.votes),
false => self.nays = self.nays.saturating_sub(delegations.votes),
}
Some(())
}
}
#[derive(Encode, MaxEncodedLen, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
pub struct ReferendumStatus<BlockNumber, Proposal, Balance> {
pub end: BlockNumber,
pub proposal: Proposal,
pub threshold: VoteThreshold,
pub delay: BlockNumber,
pub tally: Tally<Balance>,
}
#[derive(Encode, MaxEncodedLen, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
pub enum ReferendumInfo<BlockNumber, Proposal, Balance> {
Ongoing(ReferendumStatus<BlockNumber, Proposal, Balance>),
Finished { approved: bool, end: BlockNumber },
}
impl<BlockNumber, Proposal, Balance: Default> ReferendumInfo<BlockNumber, Proposal, Balance> {
pub fn new(
end: BlockNumber,
proposal: Proposal,
threshold: VoteThreshold,
delay: BlockNumber,
) -> Self {
let s = ReferendumStatus { end, proposal, threshold, delay, tally: Tally::default() };
ReferendumInfo::Ongoing(s)
}
}
pub enum UnvoteScope {
Any,
OnlyExpired,
}