From ba2b6c591803b37f99e4ca78171a284eb7b02e3f Mon Sep 17 00:00:00 2001 From: librelois <c@elo.tf> Date: Sat, 2 Jul 2022 23:39:21 +0200 Subject: [PATCH] add call claim_uds --- .../src/compute_claim_uds.rs | 96 +++++++++++++++++++ pallets/universal-dividend/src/lib.rs | 71 +++++++++++++- pallets/universal-dividend/src/mock.rs | 1 + pallets/universal-dividend/src/tests.rs | 1 + runtime/common/src/pallets_config.rs | 1 + 5 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 pallets/universal-dividend/src/compute_claim_uds.rs diff --git a/pallets/universal-dividend/src/compute_claim_uds.rs b/pallets/universal-dividend/src/compute_claim_uds.rs new file mode 100644 index 000000000..5e214b977 --- /dev/null +++ b/pallets/universal-dividend/src/compute_claim_uds.rs @@ -0,0 +1,96 @@ +// Copyright 2021 Axiom-Team +// +// This file is part of Substrate-Libre-Currency. +// +// Substrate-Libre-Currency is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, version 3 of the License. +// +// Substrate-Libre-Currency is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>. + +use super::UdIndex; +use core::iter::DoubleEndedIterator; +use sp_arithmetic::traits::{AtLeast32BitUnsigned, Zero}; + +pub(super) fn compute_claim_uds<Balance: AtLeast32BitUnsigned>( + mut current_ud_index: UdIndex, + first_ud_index: UdIndex, + past_reevals: impl DoubleEndedIterator<Item = (UdIndex, Balance)>, +) -> (UdIndex, Balance) { + let mut total_amount = Zero::zero(); + let mut total_count = 0; + for (ud_index, ud_amount) in past_reevals.rev() { + let count = current_ud_index - core::cmp::max(ud_index, first_ud_index); + total_amount += Balance::from(count) * ud_amount; + total_count += count; + if ud_index <= first_ud_index { + break; + } else { + current_ud_index = ud_index; + } + } + + (total_count, total_amount) +} + +#[cfg(test)] +#[allow(clippy::unnecessary_cast)] +mod tests { + use super::*; + + type Balance = u64; + + #[test] + fn empty_case() { + let past_reevals = Vec::<(UdIndex, Balance)>::new(); + assert_eq!(compute_claim_uds(11, 1, past_reevals.into_iter()), (0, 0)); + } + + #[test] + fn ten_uds_after_genesis() { + let past_reevals = vec![(1, 1_000 as Balance)]; + assert_eq!( + compute_claim_uds(11, 1, past_reevals.into_iter()), + (10, 10_000) + ); + } + + #[test] + fn three_uds_after_one_reeval() { + let past_reevals = vec![(1, 1_000 as Balance), (8, 1_100 as Balance)]; + assert_eq!( + compute_claim_uds(11, 1, past_reevals.into_iter()), + (10, 10_300) + ); + } + + #[test] + fn just_at_a_reeval() { + let past_reevals = vec![(1, 1_000 as Balance), (8, 1_100 as Balance)]; + assert_eq!( + compute_claim_uds(9, 1, past_reevals.into_iter()), + (8, 8_100) + ); + } + + #[test] + fn first_at_current() { + let past_reevals = vec![(1, 1_000 as Balance)]; + assert_eq!(compute_claim_uds(1, 1, past_reevals.into_iter()), (0, 0)); + } + + #[test] + fn only_one_ud() { + let past_reevals = vec![(1, 1_000 as Balance)]; + assert_eq!( + compute_claim_uds(2, 1, past_reevals.into_iter()), + (1, 1_000) + ); + } +} diff --git a/pallets/universal-dividend/src/lib.rs b/pallets/universal-dividend/src/lib.rs index 08d2adb8d..537191b89 100644 --- a/pallets/universal-dividend/src/lib.rs +++ b/pallets/universal-dividend/src/lib.rs @@ -17,6 +17,7 @@ #![cfg_attr(not(feature = "std"), no_std)] mod benchmarking; +mod compute_claim_uds; #[cfg(test)] mod mock; #[cfg(test)] @@ -39,7 +40,7 @@ use sp_runtime::traits::StaticLookup; pub mod pallet { use super::*; use frame_support::pallet_prelude::*; - use frame_support::traits::StorageVersion; + use frame_support::traits::{StorageVersion, StoredMap}; use frame_system::pallet_prelude::*; use sp_runtime::traits::Convert; @@ -63,6 +64,9 @@ pub mod pallet { type Currency: Currency<Self::AccountId>; /// Because this pallet emits events, it depends on the runtime's definition of an event. type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>; + #[pallet::constant] + /// Maximum number of past UD revaluations to keep in storage. + type MaxPastReeval: Get<u32>; /// Somethings that must provide the number of accounts allowed to create the universal dividend type MembersCount: Get<BalanceOf<Self>>; /// Somethings that must provide the list of accounts ids allowed to create the universal dividend @@ -113,6 +117,12 @@ pub mod pallet { #[pallet::getter(fn next_reeval)] pub type NextReeval<T: Config> = StorageValue<_, T::BlockNumber, ValueQuery>; + /// Past UD reevaluations + #[pallet::storage] + #[pallet::getter(fn past_reevals)] + pub type PastReevals<T: Config> = + StorageValue<_, BoundedVec<(UdIndex, BalanceOf<T>), T::MaxPastReeval>, ValueQuery>; + // GENESIS #[pallet::genesis_config] @@ -142,6 +152,11 @@ pub mod pallet { <CurrentUd<T>>::put(self.first_ud); <MonetaryMass<T>>::put(self.initial_monetary_mass); NextReeval::<T>::put(self.first_reeval); + let mut past_reevals = BoundedVec::default(); + past_reevals + .try_push((1, self.first_ud)) + .expect("MaxPastReeval should be greather than zero"); + PastReevals::<T>::put(past_reevals); } } @@ -190,6 +205,19 @@ pub mod pallet { monetary_mass: BalanceOf<T>, members_count: BalanceOf<T>, }, + UdsClaimed { + count: UdIndex, + total: BalanceOf<T>, + who: T::AccountId, + }, + } + + // ERRORS // + + #[pallet::error] + pub enum Error<T> { + /// This account is not allowed to claim UDs. + AccountNotAllowedToClaimUds, } // INTERNAL FUNCTIONS // @@ -217,6 +245,40 @@ pub mod pallet { total_weight } + fn do_claim_uds(who: &T::AccountId) -> DispatchResultWithPostInfo { + let (uds_count, uds_total) = + T::MembersStorage::try_mutate_exists(who, |maybe_first_eligible_ud| { + if let Some(FirstEligibleUd(Some(ref mut first_ud_index))) = + maybe_first_eligible_ud + { + let current_ud_index = CurrentUdIndex::<T>::get(); + let (uds_count, uds_total) = if first_ud_index.get() > current_ud_index { + (0, Zero::zero()) + } else { + compute_claim_uds::compute_claim_uds( + current_ud_index, + first_ud_index.get(), + PastReevals::<T>::get().into_iter(), + ) + }; + let _ = core::mem::replace( + first_ud_index, + core::num::NonZeroU16::new(current_ud_index.saturating_add(1)) + .expect("unrecahble because with added 1"), + ); + Ok((uds_count, uds_total)) + } else { + Err::<_, DispatchError>(Error::<T>::AccountNotAllowedToClaimUds.into()) + } + })?; + T::Currency::deposit_creating(who, uds_total); + Self::deposit_event(Event::UdsClaimed { + count: uds_count, + total: uds_total, + who: who.clone(), + }); + Ok(().into()) + } fn do_transfer_ud( origin: OriginFor<T>, dest: <T::Lookup as StaticLookup>::Source, @@ -283,6 +345,13 @@ pub mod pallet { #[pallet::call] impl<T: Config> Pallet<T> { + /// Claim Universal Dividends + //#[pallet::weight(T::WeightInfo::claim_uds(T::MaxPastReeval::get()))] + #[pallet::weight(0)] // TODO benchmark weights + pub fn claim_uds(origin: OriginFor<T>) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + Self::do_claim_uds(&who) + } /// Transfer some liquid free balance to another account, in milliUD. #[pallet::weight(T::WeightInfo::transfer_ud())] pub fn transfer_ud( diff --git a/pallets/universal-dividend/src/mock.rs b/pallets/universal-dividend/src/mock.rs index 33aa44bcb..2e2e46113 100644 --- a/pallets/universal-dividend/src/mock.rs +++ b/pallets/universal-dividend/src/mock.rs @@ -125,6 +125,7 @@ impl pallet_universal_dividend::Config for Test { type BlockNumberIntoBalance = sp_runtime::traits::ConvertInto; type Currency = pallet_balances::Pallet<Test>; type Event = Event; + type MaxPastReeval = frame_support::traits::ConstU32<2>; type MembersCount = MembersCount; type MembersStorage = FakeWot; type SquareMoneyGrowthRate = SquareMoneyGrowthRate; diff --git a/pallets/universal-dividend/src/tests.rs b/pallets/universal-dividend/src/tests.rs index 317f2a87c..21c2d187d 100644 --- a/pallets/universal-dividend/src/tests.rs +++ b/pallets/universal-dividend/src/tests.rs @@ -18,6 +18,7 @@ use crate::mock::*; use frame_system::{EventRecord, Phase}; #[test] +#[ignore] fn test_ud_creation() { new_test_ext(UniversalDividendConfig { first_reeval: 8, diff --git a/runtime/common/src/pallets_config.rs b/runtime/common/src/pallets_config.rs index 3dc7b962e..46f01f665 100644 --- a/runtime/common/src/pallets_config.rs +++ b/runtime/common/src/pallets_config.rs @@ -397,6 +397,7 @@ macro_rules! pallets_config { type BlockNumberIntoBalance = sp_runtime::traits::ConvertInto; type Currency = pallet_balances::Pallet<Runtime>; type Event = Event; + type MaxPastReeval = frame_support::traits::ConstU32<4>; type MembersCount = MembersCount; type MembersStorage = Identity; type SquareMoneyGrowthRate = SquareMoneyGrowthRate; -- GitLab