#![cfg_attr(not(feature = "std"), no_std)]
mod mock;
mod tests;
use core::slice::Iter;
use eq_balances::BalanceGetter;
use eq_primitives::{currency::Currency, Aggregates, SignedBalance, TotalAggregates, UserGroup};
use eq_utils::log::eq_log;
use frame_support::codec::{Codec, Decode, Encode, FullCodec};
use frame_support::{
debug, decl_error, decl_event, decl_module, decl_storage, dispatch::DispatchResult, ensure,
storage::IterableStorageMap, Parameter,
};
use frame_system::ensure_root;
#[cfg(feature = "std")]
use serde::{Deserialize, Serialize};
use sp_runtime::{
traits::{AtLeast32BitUnsigned, MaybeSerializeDeserialize, Member, Zero},
RuntimeDebug,
};
use sp_std::fmt::Debug;
use sp_std::iter::Iterator;
use sp_std::prelude::*;
pub trait Trait: frame_system::Trait {
type Balance: Parameter
+ Member
+ AtLeast32BitUnsigned
+ Codec
+ Default
+ Copy
+ MaybeSerializeDeserialize
+ Debug
+ From<u64>
+ Into<u64>;
type BalanceGetter: BalanceGetter<Self::AccountId, Self::Balance>;
}
decl_storage! {
trait Store for Module<T: Trait> as EqAggregates {
pub AccountUserGroups get (fn account_user_groups): double_map hasher(blake2_128_concat) UserGroup, hasher(blake2_128_concat) T::AccountId => bool;
pub TotalUserGroups get (fn total_user_groups): double_map hasher(blake2_128_concat) UserGroup, hasher(blake2_128_concat) Currency => TotalAggregates<T::Balance>;
}
}
decl_error! {
pub enum Error for Module<T: Trait> {
AlreadyAdded,
AlreadyRemoved,
}
}
decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
}
impl<T: Trait> Module<T> {
fn in_usergroup(account_id: &T::AccountId, user_group: &UserGroup) -> bool {
let acc = <AccountUserGroups<T>>::get(&user_group, &account_id);
acc
}
fn set_usergroup(account_id: &T::AccountId, user_group: &UserGroup, is_in: &bool) {
if *is_in == <AccountUserGroups<T>>::get(user_group, account_id) {
return;
}
if *is_in {
<AccountUserGroups<T>>::insert(user_group, account_id, true);
} else {
<AccountUserGroups<T>>::remove(user_group, account_id);
}
for (currency, signed_balance) in T::BalanceGetter::iterate_account_balances(account_id) {
if *is_in {
Self::update_group_total(
account_id,
¤cy,
&SignedBalance::zero(),
&signed_balance,
user_group,
);
} else {
Self::update_group_total(
account_id,
¤cy,
&signed_balance,
&signed_balance.negate(),
user_group,
);
}
}
}
fn update_group_total(
account_id: &T::AccountId,
currency: &Currency,
prev_balance: &SignedBalance<T::Balance>,
delta_balance: &SignedBalance<T::Balance>,
user_group: &UserGroup,
) {
match delta_balance {
SignedBalance::Positive(delta) => match prev_balance {
SignedBalance::Negative(prev) => {
TotalUserGroups::<T>::mutate(user_group, currency, |total| {
let aggregates_change = prev.min(delta);
eq_log!(
"delta = +{:?} prev = -{:?} change= {:?}",
delta,
prev,
aggregates_change
);
total.collateral += *delta - *aggregates_change;
total.debt -= *aggregates_change;
});
}
SignedBalance::Positive(prev) => {
eq_log!("delta = +{:?} prev = +{:?}", delta, prev,);
TotalUserGroups::<T>::mutate(user_group, currency, |total| {
total.collateral += *delta;
});
}
},
SignedBalance::Negative(delta) => match prev_balance {
SignedBalance::Negative(prev) => {
eq_log!("delta = -{:?} prev = -{:?}", delta, prev,);
TotalUserGroups::<T>::mutate(user_group, currency, |total| {
total.debt += *delta;
});
}
SignedBalance::Positive(prev) => {
let aggregates_change = prev.min(delta);
TotalUserGroups::<T>::mutate(user_group, currency, |total| {
eq_log!(
"delta = -{:?} prev = +{:?} change= {:?} total={:?}",
delta,
prev,
aggregates_change,
total,
);
total.collateral -= *aggregates_change;
total.debt += *delta - *aggregates_change;
eq_log!("newtotal={:?}", total,);
});
}
},
}
}
fn update_total(
account_id: &T::AccountId,
currency: &Currency,
prev_balance: &SignedBalance<T::Balance>,
delta_balance: &SignedBalance<T::Balance>,
) {
for group in UserGroup::iterator() {
if <AccountUserGroups<T>>::get(&group, &account_id) {
Self::update_group_total(account_id, currency, prev_balance, delta_balance, &group);
}
}
}
fn iter_account(user_group: &UserGroup) -> Box<dyn Iterator<Item = T::AccountId>> {
Box::new(<AccountUserGroups<T>>::iter_prefix(user_group).map(|(k, v)| k))
}
fn iter_total(
user_group: &UserGroup,
) -> Box<dyn Iterator<Item = (Currency, TotalAggregates<T::Balance>)>> {
Box::new(<TotalUserGroups<T>>::iter_prefix(user_group))
}
fn get_total(user_group: &UserGroup, currency: &Currency) -> TotalAggregates<T::Balance> {
<TotalUserGroups<T>>::get(user_group, currency)
}
}
impl<T: Trait> Aggregates<T::AccountId, T::Balance> for Module<T> {
fn in_usergroup(account_id: &T::AccountId, user_group: &UserGroup) -> bool {
Self::in_usergroup(account_id, user_group)
}
fn set_usergroup(account_id: &T::AccountId, user_group: &UserGroup, is_in: &bool) {
Self::set_usergroup(account_id, user_group, is_in)
}
fn update_total(
account_id: &T::AccountId,
currency: &Currency,
prev_balance: &SignedBalance<T::Balance>,
delta_balance: &SignedBalance<T::Balance>,
) {
Self::update_total(account_id, currency, prev_balance, delta_balance)
}
fn iter_account(user_group: &UserGroup) -> Box<dyn Iterator<Item = T::AccountId>> {
Self::iter_account(user_group)
}
fn iter_total(
user_group: &UserGroup,
) -> Box<dyn Iterator<Item = (Currency, TotalAggregates<T::Balance>)>> {
Self::iter_total(user_group)
}
fn get_total(user_group: &UserGroup, currency: &Currency) -> TotalAggregates<T::Balance> {
Self::get_total(user_group, currency)
}
}