git.delta.rocks / unique-network / refs/commits / 834c75ed844b

difftreelog

change unstake and on_initrialize logicc and + added `Reserved`

PraetorP2022-09-02parent: #492b651.patch.diff
in: master

7 files changed

modifiedclient/rpc/src/lib.rsdiffbeforeafterboth
--- a/client/rpc/src/lib.rs
+++ b/client/rpc/src/lib.rs
@@ -250,14 +250,17 @@
 
 mod app_promotion_unique_rpc {
 	use super::*;
-	
+
 	#[rpc(server)]
 	#[async_trait]
 	pub trait AppPromotionApi<BlockHash, BlockNumber, CrossAccountId, AccountId> {
 		/// Returns the total amount of staked tokens.
 		#[method(name = "appPromotion_totalStaked")]
-		fn total_staked(&self, staker: Option<CrossAccountId>, at: Option<BlockHash>)
-			-> Result<String>;
+		fn total_staked(
+			&self,
+			staker: Option<CrossAccountId>,
+			at: Option<BlockHash>,
+		) -> Result<String>;
 
 		///Returns the total amount of staked tokens per block when staked.
 		#[method(name = "appPromotion_totalStakedPerBlock")]
@@ -269,8 +272,11 @@
 
 		/// Returns the total amount locked by staking tokens.
 		#[method(name = "appPromotion_totalStakingLocked")]
-		fn total_staking_locked(&self, staker: CrossAccountId, at: Option<BlockHash>)
-			-> Result<String>;
+		fn total_staking_locked(
+			&self,
+			staker: CrossAccountId,
+			at: Option<BlockHash>,
+		) -> Result<String>;
 
 		/// Returns the total amount of tokens pending withdrawal from staking.
 		#[method(name = "appPromotion_pendingUnstake")]
@@ -590,8 +596,12 @@
 }
 
 impl<C, Block, BlockNumber, CrossAccountId, AccountId>
- 	app_promotion_unique_rpc::AppPromotionApiServer<<Block as BlockT>::Hash, BlockNumber, CrossAccountId, AccountId>
-	for AppPromotion<C, Block>
+	app_promotion_unique_rpc::AppPromotionApiServer<
+		<Block as BlockT>::Hash,
+		BlockNumber,
+		CrossAccountId,
+		AccountId,
+	> for AppPromotion<C, Block>
 where
 	Block: BlockT,
 	BlockNumber: Decode + Member + AtLeast32BitUnsigned,
modifiednode/rpc/src/lib.rsdiffbeforeafterboth
--- a/node/rpc/src/lib.rs
+++ b/node/rpc/src/lib.rs
@@ -148,7 +148,12 @@
 	C::Api: fp_rpc::ConvertTransactionRuntimeApi<Block>,
 	C::Api:
 		up_rpc::UniqueApi<Block, BlockNumber, <R as RuntimeInstance>::CrossAccountId, AccountId>,
-	C::Api: app_promotion_rpc::AppPromotionApi<Block, BlockNumber, <R as RuntimeInstance>::CrossAccountId, AccountId>,
+	C::Api: app_promotion_rpc::AppPromotionApi<
+		Block,
+		BlockNumber,
+		<R as RuntimeInstance>::CrossAccountId,
+		AccountId,
+	>,
 	C::Api: rmrk_rpc::RmrkApi<
 		Block,
 		AccountId,
modifiedpallets/app-promotion/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/app-promotion/src/benchmarking.rs
+++ b/pallets/app-promotion/src/benchmarking.rs
@@ -39,13 +39,13 @@
 		T::BlockNumber: From<u32> + Into<u32>,
 		<<T as Config>::Currency as Currency<T::AccountId>>::Balance: Sum + From<u128>
 	}
-	start_app_promotion {
+	// start_app_promotion {
 
-	} : {PromototionPallet::<T>::start_app_promotion(RawOrigin::Root.into(), None)?}
+	// } : {PromototionPallet::<T>::start_app_promotion(RawOrigin::Root.into(), None)?}
 
-	stop_app_promotion{
-		PromototionPallet::<T>::start_app_promotion(RawOrigin::Root.into(), Some(25.into()))?;
-	} : {PromototionPallet::<T>::stop_app_promotion(RawOrigin::Root.into())?}
+	// stop_app_promotion{
+	// 	PromototionPallet::<T>::start_app_promotion(RawOrigin::Root.into(), Some(25.into()))?;
+	// } : {PromototionPallet::<T>::stop_app_promotion(RawOrigin::Root.into())?}
 
 	set_admin_address {
 		let pallet_admin = account::<T::AccountId>("admin", 0, SEED);
@@ -58,6 +58,10 @@
 		PromototionPallet::<T>::set_admin_address(RawOrigin::Root.into(), T::CrossAccountId::from_sub(pallet_admin.clone()))?;
 		let _ = <T as Config>::Currency::make_free_balance_be(&pallet_admin,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());
 		let staker: T::AccountId = account("caller", 0, SEED);
+		let stakers: Vec<T::AccountId> = (0..100).map(|index| account("staker", index, SEED)).collect();
+		stakers.iter().for_each(|staker| {
+			<T as Config>::Currency::make_free_balance_be(&staker,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());
+		});
 		let _ = <T as Config>::Currency::make_free_balance_be(&staker,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());
 		let _ = PromototionPallet::<T>::stake(RawOrigin::Signed(staker.clone()).into(), share * <T as Config>::Currency::total_balance(&staker))?;
 	} : {PromototionPallet::<T>::payout_stakers(RawOrigin::Signed(pallet_admin.clone()).into(), Some(1))?}
@@ -70,9 +74,9 @@
 
 	unstake {
 		let caller = account::<T::AccountId>("caller", 0, SEED);
-		let share = Perbill::from_rational(1u32, 10);
+		let share = Perbill::from_rational(1u32, 20);
 		let _ = <T as Config>::Currency::make_free_balance_be(&caller,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());
-		let _ = PromototionPallet::<T>::stake(RawOrigin::Signed(caller.clone()).into(), share * <T as Config>::Currency::total_balance(&caller))?;
+		(0..10).map(|_| PromototionPallet::<T>::stake(RawOrigin::Signed(caller.clone()).into(), share * <T as Config>::Currency::total_balance(&caller))).collect::<Result<Vec<_>, _>>()?;
 
 	} : {PromototionPallet::<T>::unstake(RawOrigin::Signed(caller.clone()).into())?}
 
modifiedpallets/app-promotion/src/lib.rsdiffbeforeafterboth
36pub mod types;36pub mod types;
37pub mod weights;37pub mod weights;
3838
39use sp_std::{vec::Vec, iter::Sum, borrow::ToOwned};39use sp_std::{
40 vec::{Vec},
41 vec,
42 iter::Sum,
43 borrow::ToOwned,
44};
40use sp_core::H160;45use sp_core::H160;
41use codec::EncodeLike;46use codec::EncodeLike;
42use pallet_balances::BalanceLock;47use pallet_balances::BalanceLock;
63 ArithmeticError,68 ArithmeticError,
64};69};
6570
71pub const LOCK_IDENTIFIER: [u8; 8] = *b"appstake";
72const PENDING_LIMIT_PER_BLOCK: u32 = 3;
73
66type BalanceOf<T> =74type BalanceOf<T> =
67 <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;75 <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
6876
72// const TWO_WEEK: u32 = 2 * WEEK;80// const TWO_WEEK: u32 = 2 * WEEK;
73// const YEAR: u32 = DAY * 365;81// const YEAR: u32 = DAY * 365;
7482
75pub const LOCK_IDENTIFIER: [u8; 8] = *b"appstake";
76
77#[frame_support::pallet]83#[frame_support::pallet]
78pub mod pallet {84pub mod pallet {
79 use super::*;85 use super::*;
80 use frame_support::{Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key, PalletId};86 use frame_support::{
87 Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key, PalletId,
88 traits::ReservableCurrency,
89 };
81 use frame_system::pallet_prelude::*;90 use frame_system::pallet_prelude::*;
8291
83 #[pallet::config]92 #[pallet::config]
84 pub trait Config: frame_system::Config + pallet_evm::account::Config {93 pub trait Config: frame_system::Config + pallet_evm::account::Config {
85 type Currency: ExtendedLockableCurrency<Self::AccountId>;94 type Currency: ExtendedLockableCurrency<Self::AccountId>
95 + ReservableCurrency<Self::AccountId>;
8696
87 type CollectionHandler: CollectionHandler<97 type CollectionHandler: CollectionHandler<
88 AccountId = Self::AccountId,98 AccountId = Self::AccountId,
149 NoPermission,159 NoPermission,
150 /// Insufficient funds to perform an action160 /// Insufficient funds to perform an action
151 NotSufficientFounds,161 NotSufficientFounds,
162 PendingForBlockOverflow,
152 /// An error related to the fact that an invalid argument was passed to perform an action163 /// An error related to the fact that an invalid argument was passed to perform an action
153 InvalidArgument,164 InvalidArgument,
154 }165 }
175 StorageMap<_, Blake2_128Concat, T::AccountId, u8, ValueQuery>;186 StorageMap<_, Blake2_128Concat, T::AccountId, u8, ValueQuery>;
176187
177 /// Amount of tokens pending unstake per user per block.188 /// Amount of tokens pending unstake per user per block.
189 // #[pallet::storage]
190 // pub type PendingUnstake<T: Config> = StorageNMap<
191 // Key = (
192 // Key<Blake2_128Concat, T::AccountId>,
193 // Key<Twox64Concat, T::BlockNumber>,
194 // ),
195 // Value = BalanceOf<T>,
196 // QueryKind = ValueQuery,
197 // >;
178 #[pallet::storage]198 #[pallet::storage]
179 pub type PendingUnstake<T: Config> = StorageNMap<199 pub type PendingUnstake<T: Config> = StorageMap<
180 Key = (200 _,
181 Key<Blake2_128Concat, T::AccountId>,201 Twox64Concat,
202 T::BlockNumber,
182 Key<Twox64Concat, T::BlockNumber>,203 BoundedVec<(T::AccountId, BalanceOf<T>), ConstU32<PENDING_LIMIT_PER_BLOCK>>,
183 ),
184 Value = BalanceOf<T>,
185 QueryKind = ValueQuery,204 ValueQuery,
186 >;205 >;
187206
188 /// A block when app-promotion has started .I think this is redundant, because we only need `NextInterestBlock`.207 /// A block when app-promotion has started .I think this is redundant, because we only need `NextInterestBlock`.
189 #[pallet::storage]208 #[pallet::storage]
190 pub type StartBlock<T: Config> = StorageValue<Value = T::BlockNumber, QueryKind = ValueQuery>;209 pub type StartBlock<T: Config> = StorageValue<Value = T::BlockNumber, QueryKind = ValueQuery>;
191210
192 /// Next target block when interest is recalculated211 // /// Next target block when interest is recalculated
193 #[pallet::storage]212 // #[pallet::storage]
194 #[pallet::getter(fn get_interest_block)]213 // #[pallet::getter(fn get_interest_block)]
195 pub type NextInterestBlock<T: Config> =214 // pub type NextInterestBlock<T: Config> =
196 StorageValue<Value = T::BlockNumber, QueryKind = ValueQuery>;215 // StorageValue<Value = T::BlockNumber, QueryKind = ValueQuery>;
197216
198 /// Stores hash a record for which the last revenue recalculation was performed.217 /// Stores hash a record for which the last revenue recalculation was performed.
199 /// If `None`, then recalculation has not yet been performed or calculations have been completed for all stakers.218 /// If `None`, then recalculation has not yet been performed or calculations have been completed for all stakers.
204223
205 #[pallet::hooks]224 #[pallet::hooks]
206 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {225 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
207 fn on_initialize(current_block: T::BlockNumber) -> Weight226 fn on_initialize(current_block_number: T::BlockNumber) -> Weight
208 where227 where
209 <T as frame_system::Config>::BlockNumber: From<u32>,228 <T as frame_system::Config>::BlockNumber: From<u32>,
210 {229 {
211 let mut consumed_weight = 0;230 let mut consumed_weight = 0;
212 // let mut add_weight = |reads, writes, weight| {231 let mut add_weight = |reads, writes, weight| {
213 // consumed_weight += T::DbWeight::get().reads_writes(reads, writes);232 consumed_weight += T::DbWeight::get().reads_writes(reads, writes);
214 // consumed_weight += weight;233 consumed_weight += weight;
215 // };234 };
216235
217 let current_relay_block = T::RelayBlockNumberProvider::current_block_number();236 let block_pending = PendingUnstake::<T>::take(current_block_number);
218 PendingUnstake::<T>::iter()
219 .filter_map(|((staker, block), amount)| {
220 if block <= current_relay_block {
221 Some((staker, block, amount))
222 } else {
223 None
224 }
225 })
226 .for_each(|(staker, block, amount)| {
227 Self::unlock_balance_unchecked(&staker, amount);
228 <PendingUnstake<T>>::remove((staker, block));
229 });
230237
231 // let next_interest_block = Self::get_interest_block();238 add_weight(0, 1, 0);
232 // let current_relay_block = T::RelayBlockNumberProvider::current_block_number();
233 // if next_interest_block != 0.into() && current_relay_block >= next_interest_block {
234 // let mut acc = <BalanceOf<T>>::default();
235 // let mut base_acc = <BalanceOf<T>>::default();
236239
237 // NextInterestBlock::<T>::set(240 if !block_pending.is_empty() {
238 // NextInterestBlock::<T>::get() + T::RecalculationInterval::get(),241 block_pending.into_iter().for_each(|(staker, amount)| {
242 <T::Currency as ReservableCurrency<T::AccountId>>::unreserve(&staker, amount);
239 // );243 });
240 // add_weight(0, 1, 0);244 }
241245
242 // Staked::<T>::iter()
243 // .filter(|((_, block), _)| {
244 // *block + T::RecalculationInterval::get() <= current_relay_block
245 // })
246 // .for_each(|((staker, block), amount)| {
247 // Self::recalculate_stake(&staker, block, amount, &mut acc);
248 // add_weight(0, 0, T::WeightInfo::recalculate_stake());
249 // base_acc += amount;
250 // });
251 // <TotalStaked<T>>::get()
252 // .checked_add(&acc)
253 // .map(|res| <TotalStaked<T>>::set(res));
254
255 // Self::deposit_event(Event::StakingRecalculation(base_acc, acc));
256 // add_weight(0, 1, 0);
257 // } else {
258 // add_weight(1, 0, 0)
259 // };
260 consumed_weight246 consumed_weight
261 }247 }
262 }248 }
275 Ok(())261 Ok(())
276 }262 }
277263
278 #[pallet::weight(T::WeightInfo::start_app_promotion())]264 // #[pallet::weight(T::WeightInfo::start_app_promotion())]
279 pub fn start_app_promotion(265 // pub fn start_app_promotion(
280 origin: OriginFor<T>,266 // origin: OriginFor<T>,
281 promotion_start_relay_block: Option<T::BlockNumber>,267 // promotion_start_relay_block: Option<T::BlockNumber>,
282 ) -> DispatchResult268 // ) -> DispatchResult
283 where269 // where
284 <T as frame_system::Config>::BlockNumber: From<u32>,270 // <T as frame_system::Config>::BlockNumber: From<u32>,
285 {271 // {
286 ensure_root(origin)?;272 // ensure_root(origin)?;
287273
288 // Start app-promotion mechanics if it has not been yet initialized274 // // Start app-promotion mechanics if it has not been yet initialized
289 if <StartBlock<T>>::get() == 0u32.into() {275 // if <StartBlock<T>>::get() == 0u32.into() {
290 let start_block = promotion_start_relay_block276 // let start_block = promotion_start_relay_block
291 .unwrap_or(T::RelayBlockNumberProvider::current_block_number());277 // .unwrap_or(T::RelayBlockNumberProvider::current_block_number());
292278
293 // Set promotion global start block279 // // Set promotion global start block
294 <StartBlock<T>>::set(start_block);280 // <StartBlock<T>>::set(start_block);
295281
296 <NextInterestBlock<T>>::set(start_block + T::RecalculationInterval::get());282 // <NextInterestBlock<T>>::set(start_block + T::RecalculationInterval::get());
297 }283 // }
298284
299 Ok(())285 // Ok(())
300 }286 // }
301287
302 #[pallet::weight(T::WeightInfo::stop_app_promotion())]288 // #[pallet::weight(T::WeightInfo::stop_app_promotion())]
303 pub fn stop_app_promotion(origin: OriginFor<T>) -> DispatchResult289 // pub fn stop_app_promotion(origin: OriginFor<T>) -> DispatchResult
304 where290 // where
305 <T as frame_system::Config>::BlockNumber: From<u32>,291 // <T as frame_system::Config>::BlockNumber: From<u32>,
306 {292 // {
307 ensure_root(origin)?;293 // ensure_root(origin)?;
308294
309 if <StartBlock<T>>::get() != 0u32.into() {295 // if <StartBlock<T>>::get() != 0u32.into() {
310 <StartBlock<T>>::set(T::BlockNumber::default());296 // <StartBlock<T>>::set(T::BlockNumber::default());
311 <NextInterestBlock<T>>::set(T::BlockNumber::default());297 // <NextInterestBlock<T>>::set(T::BlockNumber::default());
312 }298 // }
313299
314 Ok(())300 // Ok(())
315 }301 // }
316302
317 #[pallet::weight(T::WeightInfo::stake())]303 #[pallet::weight(T::WeightInfo::stake())]
318 pub fn stake(staker: OriginFor<T>, amount: BalanceOf<T>) -> DispatchResult {304 pub fn stake(staker: OriginFor<T>, amount: BalanceOf<T>) -> DispatchResult {
369 #[pallet::weight(T::WeightInfo::unstake())]355 #[pallet::weight(T::WeightInfo::unstake())]
370 pub fn unstake(staker: OriginFor<T>) -> DispatchResultWithPostInfo {356 pub fn unstake(staker: OriginFor<T>) -> DispatchResultWithPostInfo {
371 let staker_id = ensure_signed(staker)?;357 let staker_id = ensure_signed(staker)?;
358 let block = <frame_system::Pallet<T>>::block_number() + T::PendingInterval::get();
359 let mut pendings = <PendingUnstake<T>>::get(block);
372360
361 ensure!(pendings.is_full(), Error::<T>::PendingForBlockOverflow);
362
373 let mut total_stakes = 0u64;363 let mut total_stakes = 0u64;
374364
375 let total_staked: BalanceOf<T> = Staked::<T>::drain_prefix((&staker_id,))365 let total_staked: BalanceOf<T> = Staked::<T>::drain_prefix((&staker_id,))
382 if total_staked.is_zero() {372 if total_staked.is_zero() {
383 return Ok(None.into());373 return Ok(None.into());
384 }374 }
385 let block =
386 T::RelayBlockNumberProvider::current_block_number() + T::PendingInterval::get();
387 <PendingUnstake<T>>::insert(
388 (&staker_id, block),
389 <PendingUnstake<T>>::get((&staker_id, block))
390 .checked_add(&total_staked)
391 .ok_or(ArithmeticError::Overflow)?,
392 );
393375
376 pendings
377 .try_push((staker_id.clone(), total_staked))
378 .map_err(|_| Error::<T>::PendingForBlockOverflow)?;
379
380 <PendingUnstake<T>>::insert(block, pendings);
381
382 Self::unlock_balance_unchecked(&staker_id, total_staked);
383
384 <T::Currency as ReservableCurrency<T::AccountId>>::reserve(&staker_id, total_staked)?;
385
394 TotalStaked::<T>::set(386 TotalStaked::<T>::set(
395 TotalStaked::<T>::get()387 TotalStaked::<T>::get()
396 .checked_sub(&total_staked)388 .checked_sub(&total_staked)
671 <<T as Config>::Currency as Currency<T::AccountId>>::Balance: Sum,663 <<T as Config>::Currency as Currency<T::AccountId>>::Balance: Sum,
672{664{
673 pub fn cross_id_pending_unstake(staker: Option<T::CrossAccountId>) -> BalanceOf<T> {665 pub fn cross_id_pending_unstake(staker: Option<T::CrossAccountId>) -> BalanceOf<T> {
674 staker.map_or(PendingUnstake::<T>::iter_values().sum(), |s| {666 staker.map_or(
667 PendingUnstake::<T>::iter_values()
668 .flat_map(|pendings| pendings.into_iter().map(|(_, amount)| amount))
669 .sum(),
670 |s| {
675 PendingUnstake::<T>::iter_prefix_values((s.as_sub(),)).sum()671 PendingUnstake::<T>::iter_values()
672 .flatten()
673 .filter_map(|(id, amount)| {
674 if id == *s.as_sub() {
675 Some(amount)
676 } else {
677 None
678 }
679 })
680 .sum()
676 })681 },
682 )
677 }683 }
678684
679 pub fn cross_id_pending_unstake_per_block(685 pub fn cross_id_pending_unstake_per_block(
680 staker: T::CrossAccountId,686 staker: T::CrossAccountId,
681 ) -> Vec<(T::BlockNumber, BalanceOf<T>)> {687 ) -> Vec<(T::BlockNumber, BalanceOf<T>)> {
682 let mut unsorted_res = PendingUnstake::<T>::iter_prefix((staker.as_sub(),))688 let mut unsorted_res = vec![];
689 PendingUnstake::<T>::iter().for_each(|(block, pendings)| {
690 pendings.into_iter().for_each(|(id, amount)| {
683 .into_iter()691 if id == *staker.as_sub() {
684 .collect::<Vec<_>>();692 unsorted_res.push((block, amount));
693 };
694 })
695 });
696
685 unsorted_res.sort_by_key(|(block, _)| *block);697 unsorted_res.sort_by_key(|(block, _)| *block);
686 unsorted_res698 unsorted_res
modifiedpallets/app-promotion/src/tests.rsdiffbeforeafterboth
--- a/pallets/app-promotion/src/tests.rs
+++ b/pallets/app-promotion/src/tests.rs
@@ -16,20 +16,20 @@
 
 #![cfg(test)]
 #![allow(clippy::from_over_into)]
-use crate as pallet_promotion;
+// use crate as pallet_promotion;
 
-use frame_benchmarking::{add_benchmark, BenchmarkBatch};
-use frame_support::{
-	assert_ok, parameter_types,
-	traits::{Currency, OnInitialize, Everything, ConstU32},
-};
-use frame_system::RawOrigin;
-use sp_core::H256;
-use sp_runtime::{
-	traits::{BlakeTwo256, BlockNumberProvider, IdentityLookup},
-	testing::Header,
-	Perbill, Perquintill,
-};
+// use frame_benchmarking::{add_benchmark, BenchmarkBatch};
+// use frame_support::{
+// 	assert_ok, parameter_types,
+// 	traits::{Currency, OnInitialize, Everything, ConstU32},
+// };
+// use frame_system::RawOrigin;
+// use sp_core::H256;
+// use sp_runtime::{
+// 	traits::{BlakeTwo256, BlockNumberProvider, IdentityLookup},
+// 	testing::Header,
+// 	Perbill, Perquintill,
+// };
 
 // type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
 // type Block = frame_system::mocking::MockBlock<Test>;
@@ -127,24 +127,24 @@
 // 	} )
 // }
 
-#[test]
-fn test_perbill() {
-	const ONE_UNIQE: u128 = 1_000_000_000_000_000_000;
-	const SECONDS_TO_BLOCK: u32 = 12;
-	const DAY: u32 = 60 * 60 * 24 / SECONDS_TO_BLOCK;
-	const RECALCULATION_INTERVAL: u32 = 10;
-	let day_rate = Perbill::from_rational(5u64, 10_000);
-	let interval_rate =
-		Perbill::from_rational::<u64>(RECALCULATION_INTERVAL.into(), DAY.into()) * day_rate;
-	println!("{:?}", interval_rate * ONE_UNIQE + ONE_UNIQE);
-	println!("{:?}", day_rate * ONE_UNIQE);
-	println!("{:?}", Perbill::one() * ONE_UNIQE);
-	println!("{:?}", ONE_UNIQE);
-	let mut next_iters = ONE_UNIQE + interval_rate * ONE_UNIQE;
-	next_iters += interval_rate * next_iters;
-	println!("{:?}", next_iters);
-	let day_income = day_rate * ONE_UNIQE;
-	let interval_income = interval_rate * ONE_UNIQE;
-	let ratio = day_income / interval_income;
-	println!("{:?} || {:?}", ratio, DAY / RECALCULATION_INTERVAL);
-}
+// #[test]
+// fn test_perbill() {
+// 	const ONE_UNIQE: u128 = 1_000_000_000_000_000_000;
+// 	const SECONDS_TO_BLOCK: u32 = 12;
+// 	const DAY: u32 = 60 * 60 * 24 / SECONDS_TO_BLOCK;
+// 	const RECALCULATION_INTERVAL: u32 = 10;
+// 	let day_rate = Perbill::from_rational(5u64, 10_000);
+// 	let interval_rate =
+// 		Perbill::from_rational::<u64>(RECALCULATION_INTERVAL.into(), DAY.into()) * day_rate;
+// 	println!("{:?}", interval_rate * ONE_UNIQE + ONE_UNIQE);
+// 	println!("{:?}", day_rate * ONE_UNIQE);
+// 	println!("{:?}", Perbill::one() * ONE_UNIQE);
+// 	println!("{:?}", ONE_UNIQE);
+// 	let mut next_iters = ONE_UNIQE + interval_rate * ONE_UNIQE;
+// 	next_iters += interval_rate * next_iters;
+// 	println!("{:?}", next_iters);
+// 	let day_income = day_rate * ONE_UNIQE;
+// 	let interval_income = interval_rate * ONE_UNIQE;
+// 	let ratio = day_income / interval_income;
+// 	println!("{:?} || {:?}", ratio, DAY / RECALCULATION_INTERVAL);
+// }
modifiedpallets/app-promotion/src/weights.rsdiffbeforeafterboth
--- a/pallets/app-promotion/src/weights.rs
+++ b/pallets/app-promotion/src/weights.rs
@@ -3,7 +3,7 @@
 //! Autogenerated weights for pallet_app_promotion
 //!
 //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
-//! DATE: 2022-08-31, STEPS: `50`, REPEAT: 80, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! DATE: 2022-09-01, STEPS: `50`, REPEAT: 80, LOW RANGE: `[]`, HIGH RANGE: `[]`
 //! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024
 
 // Executed Command:
@@ -34,8 +34,6 @@
 
 /// Weight functions needed for pallet_app_promotion.
 pub trait WeightInfo {
-	fn start_app_promotion() -> Weight;
-	fn stop_app_promotion() -> Weight;
 	fn set_admin_address() -> Weight;
 	fn payout_stakers() -> Weight;
 	fn stake() -> Weight;
@@ -49,24 +47,9 @@
 /// Weights for pallet_app_promotion using the Substrate node and recommended hardware.
 pub struct SubstrateWeight<T>(PhantomData<T>);
 impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
-	// Storage: Promotion StartBlock (r:1 w:1)
-	// Storage: ParachainSystem ValidationData (r:1 w:0)
-	// Storage: Promotion NextInterestBlock (r:0 w:1)
-	fn start_app_promotion() -> Weight {
-		(3_995_000 as Weight)
-			.saturating_add(T::DbWeight::get().reads(2 as Weight))
-			.saturating_add(T::DbWeight::get().writes(2 as Weight))
-	}
-	// Storage: Promotion StartBlock (r:1 w:1)
-	// Storage: Promotion NextInterestBlock (r:0 w:1)
-	fn stop_app_promotion() -> Weight {
-		(3_623_000 as Weight)
-			.saturating_add(T::DbWeight::get().reads(1 as Weight))
-			.saturating_add(T::DbWeight::get().writes(2 as Weight))
-	}
 	// Storage: Promotion Admin (r:0 w:1)
 	fn set_admin_address() -> Weight {
-		(1_203_000 as Weight)
+		(515_000 as Weight)
 			.saturating_add(T::DbWeight::get().writes(1 as Weight))
 	}
 	// Storage: Promotion Admin (r:1 w:0)
@@ -74,54 +57,56 @@
 	// Storage: Promotion NextCalculatedRecord (r:1 w:1)
 	// Storage: Promotion Staked (r:2 w:0)
 	fn payout_stakers() -> Weight {
-		(10_859_000 as Weight)
+		(8_475_000 as Weight)
 			.saturating_add(T::DbWeight::get().reads(5 as Weight))
 			.saturating_add(T::DbWeight::get().writes(1 as Weight))
 	}
 	// Storage: System Account (r:1 w:1)
+	// Storage: Promotion StakesPerAccount (r:1 w:1)
 	// Storage: Balances Locks (r:1 w:1)
 	// Storage: ParachainSystem ValidationData (r:1 w:0)
 	// Storage: Promotion Staked (r:1 w:1)
 	// Storage: Promotion TotalStaked (r:1 w:1)
 	fn stake() -> Weight {
-		(14_789_000 as Weight)
-			.saturating_add(T::DbWeight::get().reads(5 as Weight))
-			.saturating_add(T::DbWeight::get().writes(4 as Weight))
+		(12_266_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(6 as Weight))
+			.saturating_add(T::DbWeight::get().writes(5 as Weight))
 	}
 	// Storage: Promotion Staked (r:2 w:1)
 	// Storage: ParachainSystem ValidationData (r:1 w:0)
 	// Storage: Promotion PendingUnstake (r:1 w:1)
 	// Storage: Promotion TotalStaked (r:1 w:1)
+	// Storage: Promotion StakesPerAccount (r:0 w:1)
 	fn unstake() -> Weight {
-		(16_889_000 as Weight)
+		(10_663_000 as Weight)
 			.saturating_add(T::DbWeight::get().reads(5 as Weight))
-			.saturating_add(T::DbWeight::get().writes(3 as Weight))
+			.saturating_add(T::DbWeight::get().writes(4 as Weight))
 	}
 	// Storage: Promotion Admin (r:1 w:0)
 	// Storage: Common CollectionById (r:1 w:1)
 	fn sponsor_collection() -> Weight {
-		(18_377_000 as Weight)
+		(10_879_000 as Weight)
 			.saturating_add(T::DbWeight::get().reads(2 as Weight))
 			.saturating_add(T::DbWeight::get().writes(1 as Weight))
 	}
 	// Storage: Promotion Admin (r:1 w:0)
 	// Storage: Common CollectionById (r:1 w:1)
 	fn stop_sponsoring_collection() -> Weight {
-		(13_989_000 as Weight)
+		(10_548_000 as Weight)
 			.saturating_add(T::DbWeight::get().reads(2 as Weight))
 			.saturating_add(T::DbWeight::get().writes(1 as Weight))
 	}
 	// Storage: Promotion Admin (r:1 w:0)
 	// Storage: EvmContractHelpers Sponsoring (r:0 w:1)
 	fn sponsor_contract() -> Weight {
-		(4_162_000 as Weight)
+		(2_130_000 as Weight)
 			.saturating_add(T::DbWeight::get().reads(1 as Weight))
 			.saturating_add(T::DbWeight::get().writes(1 as Weight))
 	}
 	// Storage: Promotion Admin (r:1 w:0)
 	// Storage: EvmContractHelpers Sponsoring (r:1 w:1)
 	fn stop_sponsoring_contract() -> Weight {
-		(5_457_000 as Weight)
+		(3_509_000 as Weight)
 			.saturating_add(T::DbWeight::get().reads(2 as Weight))
 			.saturating_add(T::DbWeight::get().writes(1 as Weight))
 	}
@@ -129,24 +114,9 @@
 
 // For backwards compatibility and tests
 impl WeightInfo for () {
-	// Storage: Promotion StartBlock (r:1 w:1)
-	// Storage: ParachainSystem ValidationData (r:1 w:0)
-	// Storage: Promotion NextInterestBlock (r:0 w:1)
-	fn start_app_promotion() -> Weight {
-		(3_995_000 as Weight)
-			.saturating_add(RocksDbWeight::get().reads(2 as Weight))
-			.saturating_add(RocksDbWeight::get().writes(2 as Weight))
-	}
-	// Storage: Promotion StartBlock (r:1 w:1)
-	// Storage: Promotion NextInterestBlock (r:0 w:1)
-	fn stop_app_promotion() -> Weight {
-		(3_623_000 as Weight)
-			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
-			.saturating_add(RocksDbWeight::get().writes(2 as Weight))
-	}
 	// Storage: Promotion Admin (r:0 w:1)
 	fn set_admin_address() -> Weight {
-		(1_203_000 as Weight)
+		(515_000 as Weight)
 			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
 	}
 	// Storage: Promotion Admin (r:1 w:0)
@@ -154,54 +124,56 @@
 	// Storage: Promotion NextCalculatedRecord (r:1 w:1)
 	// Storage: Promotion Staked (r:2 w:0)
 	fn payout_stakers() -> Weight {
-		(10_859_000 as Weight)
+		(8_475_000 as Weight)
 			.saturating_add(RocksDbWeight::get().reads(5 as Weight))
 			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
 	}
 	// Storage: System Account (r:1 w:1)
+	// Storage: Promotion StakesPerAccount (r:1 w:1)
 	// Storage: Balances Locks (r:1 w:1)
 	// Storage: ParachainSystem ValidationData (r:1 w:0)
 	// Storage: Promotion Staked (r:1 w:1)
 	// Storage: Promotion TotalStaked (r:1 w:1)
 	fn stake() -> Weight {
-		(14_789_000 as Weight)
-			.saturating_add(RocksDbWeight::get().reads(5 as Weight))
-			.saturating_add(RocksDbWeight::get().writes(4 as Weight))
+		(12_266_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(6 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(5 as Weight))
 	}
 	// Storage: Promotion Staked (r:2 w:1)
 	// Storage: ParachainSystem ValidationData (r:1 w:0)
 	// Storage: Promotion PendingUnstake (r:1 w:1)
 	// Storage: Promotion TotalStaked (r:1 w:1)
+	// Storage: Promotion StakesPerAccount (r:0 w:1)
 	fn unstake() -> Weight {
-		(16_889_000 as Weight)
+		(10_663_000 as Weight)
 			.saturating_add(RocksDbWeight::get().reads(5 as Weight))
-			.saturating_add(RocksDbWeight::get().writes(3 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(4 as Weight))
 	}
 	// Storage: Promotion Admin (r:1 w:0)
 	// Storage: Common CollectionById (r:1 w:1)
 	fn sponsor_collection() -> Weight {
-		(18_377_000 as Weight)
+		(10_879_000 as Weight)
 			.saturating_add(RocksDbWeight::get().reads(2 as Weight))
 			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
 	}
 	// Storage: Promotion Admin (r:1 w:0)
 	// Storage: Common CollectionById (r:1 w:1)
 	fn stop_sponsoring_collection() -> Weight {
-		(13_989_000 as Weight)
+		(10_548_000 as Weight)
 			.saturating_add(RocksDbWeight::get().reads(2 as Weight))
 			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
 	}
 	// Storage: Promotion Admin (r:1 w:0)
 	// Storage: EvmContractHelpers Sponsoring (r:0 w:1)
 	fn sponsor_contract() -> Weight {
-		(4_162_000 as Weight)
+		(2_130_000 as Weight)
 			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
 			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
 	}
 	// Storage: Promotion Admin (r:1 w:0)
 	// Storage: EvmContractHelpers Sponsoring (r:1 w:1)
 	fn stop_sponsoring_contract() -> Weight {
-		(5_457_000 as Weight)
+		(3_509_000 as Weight)
 			.saturating_add(RocksDbWeight::get().reads(2 as Weight))
 			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
 	}
modifiedruntime/common/runtime_apis.rsdiffbeforeafterboth
--- a/runtime/common/runtime_apis.rs
+++ b/runtime/common/runtime_apis.rs
@@ -197,11 +197,11 @@
                     #[cfg(feature = "app-promotion")]
                     return Ok(<pallet_app_promotion::Pallet<Runtime>>::cross_id_total_staked(staker).unwrap_or_default());
                 }
-                
+
                 fn total_staked_per_block(staker: CrossAccountId) -> Result<Vec<(BlockNumber, u128)>, DispatchError> {
                     #[cfg(not(feature = "app-promotion"))]
                     return unsupported!();
-                    
+
                     #[cfg(feature = "app-promotion")]
                     return Ok(<pallet_app_promotion::Pallet<Runtime>>::cross_id_total_staked_per_block(staker));
                 }
@@ -209,7 +209,7 @@
                 fn total_staking_locked(staker: CrossAccountId) -> Result<u128, DispatchError> {
                     #[cfg(not(feature = "app-promotion"))]
                     return unsupported!();
-                    
+
                     #[cfg(feature = "app-promotion")]
                     return Ok(<pallet_app_promotion::Pallet<Runtime>>::cross_id_locked_balance(staker));
                 }
@@ -217,7 +217,7 @@
                 fn pending_unstake(staker: Option<CrossAccountId>) -> Result<u128, DispatchError> {
                     #[cfg(not(feature = "app-promotion"))]
                     return unsupported!();
-                    
+
                     #[cfg(feature = "app-promotion")]
                     return Ok(<pallet_app_promotion::Pallet<Runtime>>::cross_id_pending_unstake(staker));
                 }
@@ -225,7 +225,7 @@
                 fn pending_unstake_per_block(staker: CrossAccountId) -> Result<Vec<(BlockNumber, u128)>, DispatchError> {
                     #[cfg(not(feature = "app-promotion"))]
                     return unsupported!();
-                    
+
                     #[cfg(feature = "app-promotion")]
                     return Ok(<pallet_app_promotion::Pallet<Runtime>>::cross_id_pending_unstake_per_block(staker))
                 }