use crate::{origin::INV4Origin, BalanceOf, Config, CoreStorage, Error, Multisig, Pallet};
use codec::{Decode, Encode, HasCompact, MaxEncodedLen};
use core::marker::PhantomData;
use frame_support::{
pallet_prelude::{Member, RuntimeDebug},
traits::{fungibles::Inspect, PollStatus, VoteTally},
BoundedBTreeMap, CloneNoBound, EqNoBound, Parameter, PartialEqNoBound, RuntimeDebugNoBound,
};
use frame_system::pallet_prelude::BlockNumberFor;
use scale_info::TypeInfo;
use sp_runtime::{
traits::{One, Zero},
DispatchError, Perbill,
};
use sp_std::vec::Vec;
pub type Votes<T> = BalanceOf<T>;
pub type Core<T> = <T as Config>::CoreId;
#[derive(
CloneNoBound,
PartialEqNoBound,
EqNoBound,
RuntimeDebugNoBound,
TypeInfo,
Encode,
Decode,
MaxEncodedLen,
)]
#[scale_info(skip_type_params(T))]
#[codec(mel_bound())]
pub struct Tally<T: Config> {
pub ayes: Votes<T>,
pub nays: Votes<T>,
pub records: BoundedBTreeMap<T::AccountId, Vote<Votes<T>>, T::MaxCallers>,
dummy: PhantomData<T>,
}
impl<T: Config> Tally<T> {
pub fn from_parts(
ayes: Votes<T>,
nays: Votes<T>,
records: BoundedBTreeMap<T::AccountId, Vote<Votes<T>>, T::MaxCallers>,
) -> Self {
Tally {
ayes,
nays,
records,
dummy: PhantomData,
}
}
pub fn process_vote(
&mut self,
account: T::AccountId,
maybe_vote: Option<Vote<Votes<T>>>,
) -> Result<Vote<Votes<T>>, DispatchError> {
let votes = if let Some(vote) = maybe_vote {
self.records
.try_insert(account, vote)
.map_err(|_| Error::<T>::MaxCallersExceeded)?;
vote
} else {
self.records.remove(&account).ok_or(Error::<T>::NotAVoter)?
};
let (ayes, nays) = self.records.values().fold(
(Zero::zero(), Zero::zero()),
|(mut ayes, mut nays): (Votes<T>, Votes<T>), vote| {
match vote {
Vote::Aye(v) => ayes += *v,
Vote::Nay(v) => nays += *v,
};
(ayes, nays)
},
);
self.ayes = ayes;
self.nays = nays;
Ok(votes)
}
}
impl<T: Config> VoteTally<Votes<T>, Core<T>> for Tally<T> {
fn new(_: Core<T>) -> Self {
Self {
ayes: Zero::zero(),
nays: Zero::zero(),
records: BoundedBTreeMap::default(),
dummy: PhantomData,
}
}
fn ayes(&self, _: Core<T>) -> Votes<T> {
self.ayes
}
fn support(&self, class: Core<T>) -> Perbill {
Perbill::from_rational(self.ayes, T::AssetsProvider::total_issuance(class))
}
fn approval(&self, _: Core<T>) -> Perbill {
Perbill::from_rational(
self.ayes,
<Votes<T> as One>::one().max(self.ayes + self.nays),
)
}
#[cfg(feature = "runtime-benchmarks")]
fn unanimity(_: Core<T>) -> Self {
todo!()
}
#[cfg(feature = "runtime-benchmarks")]
fn rejection(_: Core<T>) -> Self {
todo!()
}
#[cfg(feature = "runtime-benchmarks")]
fn from_requirements(_: Perbill, _: Perbill, _: Core<T>) -> Self {
todo!()
}
#[cfg(feature = "runtime-benchmarks")]
fn setup(_: Core<T>, _: Perbill) {
todo!()
}
}
pub trait CustomPolling<Tally> {
type Index: Ord + PartialOrd + Copy + MaxEncodedLen;
type Votes: Parameter + Ord + PartialOrd + Copy + HasCompact + MaxEncodedLen;
type Class: Parameter + Member + Ord + PartialOrd + MaxEncodedLen;
type Moment;
fn classes() -> Vec<Self::Class>;
fn as_ongoing(class: Self::Class, index: Self::Index) -> Option<(Tally, Self::Class)>;
fn access_poll<R>(
class: Self::Class,
index: Self::Index,
f: impl FnOnce(PollStatus<&mut Tally, Self::Moment, Self::Class>) -> R,
) -> R;
fn try_access_poll<R>(
class: Self::Class,
index: Self::Index,
f: impl FnOnce(PollStatus<&mut Tally, Self::Moment, Self::Class>) -> Result<R, DispatchError>,
) -> Result<R, DispatchError>;
}
impl<T: Config> CustomPolling<Tally<T>> for Pallet<T> {
type Index = T::Hash;
type Votes = Votes<T>;
type Moment = BlockNumberFor<T>;
type Class = T::CoreId;
fn classes() -> Vec<Self::Class> {
CoreStorage::<T>::iter_keys().collect()
}
fn access_poll<R>(
class: Self::Class,
index: Self::Index,
f: impl FnOnce(PollStatus<&mut Tally<T>, BlockNumberFor<T>, T::CoreId>) -> R,
) -> R {
match Multisig::<T>::get(class, index) {
Some(mut m) => {
let result = f(PollStatus::Ongoing(&mut m.tally, class));
Multisig::<T>::insert(class, index, m);
result
}
_ => f(PollStatus::None),
}
}
fn try_access_poll<R>(
class: Self::Class,
index: Self::Index,
f: impl FnOnce(
PollStatus<&mut Tally<T>, BlockNumberFor<T>, T::CoreId>,
) -> Result<R, DispatchError>,
) -> Result<R, DispatchError> {
match Multisig::<T>::get(class, index) {
Some(mut m) => {
let result = f(PollStatus::Ongoing(&mut m.tally, class))?;
Multisig::<T>::insert(class, index, m);
Ok(result)
}
_ => f(PollStatus::None),
}
}
fn as_ongoing(class: Self::Class, index: Self::Index) -> Option<(Tally<T>, T::CoreId)> {
Multisig::<T>::get(class, index).map(|m| (m.tally, class))
}
}
#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub enum Vote<Votes> {
Aye(Votes),
Nay(Votes),
}
pub type VoteRecord<T> = Vote<Votes<T>>;
impl<T: Config> Pallet<T>
where
Result<INV4Origin<T>, <T as frame_system::Config>::RuntimeOrigin>:
From<<T as frame_system::Config>::RuntimeOrigin>,
{
pub fn minimum_support_and_required_approval(core_id: T::CoreId) -> Option<(Perbill, Perbill)> {
CoreStorage::<T>::get(core_id).map(|core| (core.minimum_support, core.required_approval))
}
}