git.delta.rocks / unique-network / refs/commits / da133e176114

difftreelog

refac(app-promo): impl for `unstake_all` & `unstake_partial` extrinsics

PraetorP2023-02-14parent: #59f9c8e.patch.diff
in: master
Added benchmark for `unstake_partial` extrinsic

7 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5782,7 +5782,7 @@
 
 [[package]]
 name = "pallet-app-promotion"
-version = "0.1.4"
+version = "0.1.5"
 dependencies = [
  "frame-benchmarking",
  "frame-support",
modifiedpallets/app-promotion/CHANGELOG.mddiffbeforeafterboth
--- a/pallets/app-promotion/CHANGELOG.md
+++ b/pallets/app-promotion/CHANGELOG.md
@@ -4,6 +4,12 @@
 
 <!-- bureaucrate goes here -->
 
+## [0.1.5] - 2023-02-14
+
+### Added
+
+- `unstake_partial` extrinsic.
+
 ## [0.1.4] - 2023-01-31
 
 ### Changed
modifiedpallets/app-promotion/Cargo.tomldiffbeforeafterboth
--- a/pallets/app-promotion/Cargo.toml
+++ b/pallets/app-promotion/Cargo.toml
@@ -9,7 +9,7 @@
 license = 'GPLv3'
 name = 'pallet-app-promotion'
 repository = 'https://github.com/UniqueNetwork/unique-chain'
-version = '0.1.4'
+version = '0.1.5'
 
 [package.metadata.docs.rs]
 targets = ['x86_64-unknown-linux-gnu']
modifiedpallets/app-promotion/src/benchmarking.rsdiffbeforeafterboth
before · pallets/app-promotion/src/benchmarking.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617#![cfg(feature = "runtime-benchmarks")]1819use super::*;20use crate::Pallet as PromototionPallet;2122use sp_runtime::traits::Bounded;2324use frame_benchmarking::{benchmarks, account};25use frame_support::traits::OnInitialize;26use frame_system::RawOrigin;27use pallet_unique::benchmarking::create_nft_collection;28use pallet_evm_migration::Pallet as EvmMigrationPallet;2930const SEED: u32 = 0;3132fn set_admin<T>() -> Result<T::AccountId, sp_runtime::DispatchError>33where34	T: Config + pallet_unique::Config + pallet_evm_migration::Config,35	T::BlockNumber: From<u32> + Into<u32>,36	<<T as Config>::Currency as Currency<T::AccountId>>::Balance: Sum + From<u128>,37{38	let pallet_admin = account::<T::AccountId>("admin", 0, SEED);3940	<T as Config>::Currency::make_free_balance_be(41		&pallet_admin,42		Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value(),43	);4445	PromototionPallet::<T>::set_admin_address(46		RawOrigin::Root.into(),47		T::CrossAccountId::from_sub(pallet_admin.clone()),48	)?;4950	Ok(pallet_admin)51}5253benchmarks! {54	where_clause{55		where T:  Config + pallet_unique::Config + pallet_evm_migration::Config ,56		T::BlockNumber: From<u32> + Into<u32>,57		<<T as Config>::Currency as Currency<T::AccountId>>::Balance: Sum + From<u128>58	}5960	on_initialize {61		let b in 0..PENDING_LIMIT_PER_BLOCK;62		set_admin::<T>()?;6364		(0..b).try_for_each(|index| {65			let staker = account::<T::AccountId>("staker", index, SEED);66			<T as Config>::Currency::make_free_balance_be(&staker,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());67			PromototionPallet::<T>::stake(RawOrigin::Signed(staker.clone()).into(), Into::<BalanceOf<T>>::into(100u128) * T::Nominal::get())?;68			PromototionPallet::<T>::unstake(RawOrigin::Signed(staker.clone()).into()).map_err(|e| e.error)?;69			Result::<(), sp_runtime::DispatchError>::Ok(())70		})?;71		let block_number = <frame_system::Pallet<T>>::current_block_number() + T::PendingInterval::get();72	}: {PromototionPallet::<T>::on_initialize(block_number)}7374	set_admin_address {75		let pallet_admin = account::<T::AccountId>("admin", 0, SEED);76		let _ = <T as Config>::Currency::make_free_balance_be(&pallet_admin,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());77	} : _(RawOrigin::Root, T::CrossAccountId::from_sub(pallet_admin))7879	payout_stakers{80		let b in 1..100;8182		let pallet_admin = account::<T::AccountId>("admin", 1, SEED);83		let share = Perbill::from_rational(1u32, 20);84		PromototionPallet::<T>::set_admin_address(RawOrigin::Root.into(), T::CrossAccountId::from_sub(pallet_admin.clone()))?;85		<T as Config>::Currency::make_free_balance_be(&pallet_admin,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());86		<T as Config>::Currency::make_free_balance_be(&<T as pallet::Config>::TreasuryAccountId::get(),  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());8788		let stakers: Vec<T::AccountId> = (0..b).map(|index| account("staker", index, SEED)).collect();89		stakers.iter().for_each(|staker| {90			<T as Config>::Currency::make_free_balance_be(&staker,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());91		});92		(1..11).try_for_each(|i| {93			<frame_system::Pallet<T>>::set_block_number(i.into());94			T::RelayBlockNumberProvider::set_block_number((2*i).into());95			assert_eq!(<frame_system::Pallet<T>>::block_number(), i.into());96			assert_eq!(T::RelayBlockNumberProvider::current_block_number(), (2*i).into());97			stakers.iter()98			.map(|staker| {99				PromototionPallet::<T>::stake(RawOrigin::Signed(staker.clone()).into(), Into::<BalanceOf<T>>::into(100u128) * T::Nominal::get())100			}).collect::<Result<Vec<_>, _>>()?;101102			Result::<(), sp_runtime::DispatchError>::Ok(())103		})?;104105		let stakes = Staked::<T>::iter_prefix((&stakers[0],)).into_iter().collect::<Vec<_>>();106		assert_eq!(stakes.len(), 10);107108		<frame_system::Pallet<T>>::set_block_number(15_000.into());109		T::RelayBlockNumberProvider::set_block_number(30_000.into());110	} : _(RawOrigin::Signed(pallet_admin.clone()), Some(b as u8))111112	stake {113		let caller = account::<T::AccountId>("caller", 0, SEED);114		let share = Perbill::from_rational(1u32, 10);115		let _ = <T as Config>::Currency::make_free_balance_be(&caller,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());116	} : _(RawOrigin::Signed(caller.clone()), share * <T as Config>::Currency::total_balance(&caller))117118	unstake {119		let caller = account::<T::AccountId>("caller", 0, SEED);120		let share = Perbill::from_rational(1u32, 20);121		let _ = <T as Config>::Currency::make_free_balance_be(&caller,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());122		(1..11).map(|i| {123			// used to change block number124			<frame_system::Pallet<T>>::set_block_number(i.into());125			T::RelayBlockNumberProvider::set_block_number((2*i).into());126			assert_eq!(<frame_system::Pallet<T>>::block_number(), i.into());127			assert_eq!(T::RelayBlockNumberProvider::current_block_number(), (2*i).into());128			PromototionPallet::<T>::stake(RawOrigin::Signed(caller.clone()).into(), share * <T as Config>::Currency::total_balance(&caller))129		}).collect::<Result<Vec<_>, _>>()?;130131	} : _(RawOrigin::Signed(caller.clone()))132133	sponsor_collection {134		let pallet_admin = account::<T::AccountId>("admin", 0, SEED);135		PromototionPallet::<T>::set_admin_address(RawOrigin::Root.into(), T::CrossAccountId::from_sub(pallet_admin.clone()))?;136		let _ = <T as Config>::Currency::make_free_balance_be(&pallet_admin,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());137		let caller: T::AccountId = account("caller", 0, SEED);138		let _ = <T as Config>::Currency::make_free_balance_be(&caller,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());139		let collection = create_nft_collection::<T>(caller.clone())?;140	} : _(RawOrigin::Signed(pallet_admin.clone()), collection)141142	stop_sponsoring_collection {143		let pallet_admin = account::<T::AccountId>("admin", 0, SEED);144		PromototionPallet::<T>::set_admin_address(RawOrigin::Root.into(), T::CrossAccountId::from_sub(pallet_admin.clone()))?;145		let _ = <T as Config>::Currency::make_free_balance_be(&pallet_admin,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());146		let caller: T::AccountId = account("caller", 0, SEED);147		let _ = <T as Config>::Currency::make_free_balance_be(&caller,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());148		let collection = create_nft_collection::<T>(caller.clone())?;149		PromototionPallet::<T>::sponsor_collection(RawOrigin::Signed(pallet_admin.clone()).into(), collection)?;150	} : _(RawOrigin::Signed(pallet_admin.clone()), collection)151152	sponsor_contract {153		let pallet_admin = account::<T::AccountId>("admin", 0, SEED);154		PromototionPallet::<T>::set_admin_address(RawOrigin::Root.into(), T::CrossAccountId::from_sub(pallet_admin.clone()))?;155156		let _ = <T as Config>::Currency::make_free_balance_be(&pallet_admin,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());157		let address = H160::from_low_u64_be(SEED as u64);158		let data: Vec<u8> = (0..20 as u8).collect();159		<EvmMigrationPallet<T>>::begin(RawOrigin::Root.into(), address)?;160		<EvmMigrationPallet<T>>::finish(RawOrigin::Root.into(), address, data)?;161	} : _(RawOrigin::Signed(pallet_admin.clone()), address)162163	stop_sponsoring_contract {164		let pallet_admin = account::<T::AccountId>("admin", 0, SEED);165		PromototionPallet::<T>::set_admin_address(RawOrigin::Root.into(), T::CrossAccountId::from_sub(pallet_admin.clone()))?;166167		let _ = <T as Config>::Currency::make_free_balance_be(&pallet_admin,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());168		let address = H160::from_low_u64_be(SEED as u64);169		let data: Vec<u8> = (0..20 as u8).collect();170		<EvmMigrationPallet<T>>::begin(RawOrigin::Root.into(), address)?;171		<EvmMigrationPallet<T>>::finish(RawOrigin::Root.into(), address, data)?;172		PromototionPallet::<T>::sponsor_contract(RawOrigin::Signed(pallet_admin.clone()).into(), address)?;173	} : _(RawOrigin::Signed(pallet_admin.clone()), address)174}
after · pallets/app-promotion/src/benchmarking.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617#![cfg(feature = "runtime-benchmarks")]1819use super::*;20use crate::Pallet as PromototionPallet;2122use sp_runtime::traits::Bounded;2324use frame_benchmarking::{benchmarks, account};25use frame_support::traits::OnInitialize;26use frame_system::RawOrigin;27use pallet_unique::benchmarking::create_nft_collection;28use pallet_evm_migration::Pallet as EvmMigrationPallet;2930const SEED: u32 = 0;3132fn set_admin<T>() -> Result<T::AccountId, sp_runtime::DispatchError>33where34	T: Config + pallet_unique::Config + pallet_evm_migration::Config,35	T::BlockNumber: From<u32> + Into<u32>,36	<<T as Config>::Currency as Currency<T::AccountId>>::Balance: Sum + From<u128>,37{38	let pallet_admin = account::<T::AccountId>("admin", 0, SEED);3940	<T as Config>::Currency::make_free_balance_be(41		&pallet_admin,42		Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value(),43	);4445	PromototionPallet::<T>::set_admin_address(46		RawOrigin::Root.into(),47		T::CrossAccountId::from_sub(pallet_admin.clone()),48	)?;4950	Ok(pallet_admin)51}5253benchmarks! {54	where_clause{55		where T:  Config + pallet_unique::Config + pallet_evm_migration::Config ,56		T::BlockNumber: From<u32> + Into<u32>,57		<<T as Config>::Currency as Currency<T::AccountId>>::Balance: Sum + From<u128>58	}5960	on_initialize {61		let b in 0..PENDING_LIMIT_PER_BLOCK;62		set_admin::<T>()?;6364		(0..b).try_for_each(|index| {65			let staker = account::<T::AccountId>("staker", index, SEED);66			<T as Config>::Currency::make_free_balance_be(&staker,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());67			PromototionPallet::<T>::stake(RawOrigin::Signed(staker.clone()).into(), Into::<BalanceOf<T>>::into(100u128) * T::Nominal::get())?;68			PromototionPallet::<T>::unstake_all(RawOrigin::Signed(staker.clone()).into()).map_err(|e| e.error)?;69			Result::<(), sp_runtime::DispatchError>::Ok(())70		})?;71		let block_number = <frame_system::Pallet<T>>::current_block_number() + T::PendingInterval::get();72	}: {PromototionPallet::<T>::on_initialize(block_number)}7374	set_admin_address {75		let pallet_admin = account::<T::AccountId>("admin", 0, SEED);76		let _ = <T as Config>::Currency::make_free_balance_be(&pallet_admin,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());77	} : _(RawOrigin::Root, T::CrossAccountId::from_sub(pallet_admin))7879	payout_stakers{80		let b in 1..100;8182		let pallet_admin = account::<T::AccountId>("admin", 1, SEED);83		let share = Perbill::from_rational(1u32, 20);84		PromototionPallet::<T>::set_admin_address(RawOrigin::Root.into(), T::CrossAccountId::from_sub(pallet_admin.clone()))?;85		<T as Config>::Currency::make_free_balance_be(&pallet_admin,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());86		<T as Config>::Currency::make_free_balance_be(&<T as pallet::Config>::TreasuryAccountId::get(),  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());8788		let stakers: Vec<T::AccountId> = (0..b).map(|index| account("staker", index, SEED)).collect();89		stakers.iter().for_each(|staker| {90			<T as Config>::Currency::make_free_balance_be(&staker,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());91		});92		(1..11).try_for_each(|i| {93			<frame_system::Pallet<T>>::set_block_number(i.into());94			T::RelayBlockNumberProvider::set_block_number((2*i).into());95			assert_eq!(<frame_system::Pallet<T>>::block_number(), i.into());96			assert_eq!(T::RelayBlockNumberProvider::current_block_number(), (2*i).into());97			stakers.iter()98			.map(|staker| {99				PromototionPallet::<T>::stake(RawOrigin::Signed(staker.clone()).into(), Into::<BalanceOf<T>>::into(100u128) * T::Nominal::get())100			}).collect::<Result<Vec<_>, _>>()?;101102			Result::<(), sp_runtime::DispatchError>::Ok(())103		})?;104105		let stakes = Staked::<T>::iter_prefix((&stakers[0],)).into_iter().collect::<Vec<_>>();106		assert_eq!(stakes.len(), 10);107108		<frame_system::Pallet<T>>::set_block_number(15_000.into());109		T::RelayBlockNumberProvider::set_block_number(30_000.into());110	} : _(RawOrigin::Signed(pallet_admin.clone()), Some(b as u8))111112	stake {113		let caller = account::<T::AccountId>("caller", 0, SEED);114		let share = Perbill::from_rational(1u32, 10);115		let _ = <T as Config>::Currency::make_free_balance_be(&caller,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());116	} : _(RawOrigin::Signed(caller.clone()), share * <T as Config>::Currency::total_balance(&caller))117118	unstake_all {119		let caller = account::<T::AccountId>("caller", 0, SEED);120		let share = Perbill::from_rational(1u32, 20);121		let _ = <T as Config>::Currency::make_free_balance_be(&caller,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());122		(1..11).map(|i| {123			// used to change block number124			<frame_system::Pallet<T>>::set_block_number(i.into());125			T::RelayBlockNumberProvider::set_block_number((2*i).into());126			assert_eq!(<frame_system::Pallet<T>>::block_number(), i.into());127			assert_eq!(T::RelayBlockNumberProvider::current_block_number(), (2*i).into());128			PromototionPallet::<T>::stake(RawOrigin::Signed(caller.clone()).into(), share * <T as Config>::Currency::total_balance(&caller))129		}).collect::<Result<Vec<_>, _>>()?;130131	} : _(RawOrigin::Signed(caller.clone()))132133	unstake_partial {134		let caller = account::<T::AccountId>("caller", 0, SEED);135		let share = Perbill::from_rational(1u32, 20);136		let _ = <T as Config>::Currency::make_free_balance_be(&caller,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());137		(1..11).map(|i| {138			// used to change block number139			<frame_system::Pallet<T>>::set_block_number(i.into());140			T::RelayBlockNumberProvider::set_block_number((2*i).into());141			assert_eq!(<frame_system::Pallet<T>>::block_number(), i.into());142			assert_eq!(T::RelayBlockNumberProvider::current_block_number(), (2*i).into());143			PromototionPallet::<T>::stake(RawOrigin::Signed(caller.clone()).into(), Into::<BalanceOf<T>>::into(100u128) * T::Nominal::get())144		}).collect::<Result<Vec<_>, _>>()?;145146	} : _(RawOrigin::Signed(caller.clone()), Into::<BalanceOf<T>>::into(1000u128) * T::Nominal::get())147148	sponsor_collection {149		let pallet_admin = account::<T::AccountId>("admin", 0, SEED);150		PromototionPallet::<T>::set_admin_address(RawOrigin::Root.into(), T::CrossAccountId::from_sub(pallet_admin.clone()))?;151		let _ = <T as Config>::Currency::make_free_balance_be(&pallet_admin,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());152		let caller: T::AccountId = account("caller", 0, SEED);153		let _ = <T as Config>::Currency::make_free_balance_be(&caller,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());154		let collection = create_nft_collection::<T>(caller.clone())?;155	} : _(RawOrigin::Signed(pallet_admin.clone()), collection)156157	stop_sponsoring_collection {158		let pallet_admin = account::<T::AccountId>("admin", 0, SEED);159		PromototionPallet::<T>::set_admin_address(RawOrigin::Root.into(), T::CrossAccountId::from_sub(pallet_admin.clone()))?;160		let _ = <T as Config>::Currency::make_free_balance_be(&pallet_admin,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());161		let caller: T::AccountId = account("caller", 0, SEED);162		let _ = <T as Config>::Currency::make_free_balance_be(&caller,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());163		let collection = create_nft_collection::<T>(caller.clone())?;164		PromototionPallet::<T>::sponsor_collection(RawOrigin::Signed(pallet_admin.clone()).into(), collection)?;165	} : _(RawOrigin::Signed(pallet_admin.clone()), collection)166167	sponsor_contract {168		let pallet_admin = account::<T::AccountId>("admin", 0, SEED);169		PromototionPallet::<T>::set_admin_address(RawOrigin::Root.into(), T::CrossAccountId::from_sub(pallet_admin.clone()))?;170171		let _ = <T as Config>::Currency::make_free_balance_be(&pallet_admin,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());172		let address = H160::from_low_u64_be(SEED as u64);173		let data: Vec<u8> = (0..20 as u8).collect();174		<EvmMigrationPallet<T>>::begin(RawOrigin::Root.into(), address)?;175		<EvmMigrationPallet<T>>::finish(RawOrigin::Root.into(), address, data)?;176	} : _(RawOrigin::Signed(pallet_admin.clone()), address)177178	stop_sponsoring_contract {179		let pallet_admin = account::<T::AccountId>("admin", 0, SEED);180		PromototionPallet::<T>::set_admin_address(RawOrigin::Root.into(), T::CrossAccountId::from_sub(pallet_admin.clone()))?;181182		let _ = <T as Config>::Currency::make_free_balance_be(&pallet_admin,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());183		let address = H160::from_low_u64_be(SEED as u64);184		let data: Vec<u8> = (0..20 as u8).collect();185		<EvmMigrationPallet<T>>::begin(RawOrigin::Root.into(), address)?;186		<EvmMigrationPallet<T>>::finish(RawOrigin::Root.into(), address, data)?;187		PromototionPallet::<T>::sponsor_contract(RawOrigin::Signed(pallet_admin.clone()).into(), address)?;188	} : _(RawOrigin::Signed(pallet_admin.clone()), address)189}
modifiedpallets/app-promotion/src/lib.rsdiffbeforeafterboth
--- a/pallets/app-promotion/src/lib.rs
+++ b/pallets/app-promotion/src/lib.rs
@@ -208,6 +208,8 @@
 		SponsorNotSet,
 		/// Errors caused by incorrect actions with a locked balance.
 		IncorrectLockedBalanceOperation,
+		/// Errors caused by insufficient staked balance.
+		InsufficientStakedBalance,
 	}
 
 	/// Stores the total staked amount.
@@ -489,65 +491,30 @@
 		}
 
 		/// Unstakes all stakes.
-		/// Moves the sum of all stakes to the `reserved` state.
 		/// After the end of `PendingInterval` this sum becomes completely
 		/// free for further use.
 		#[pallet::call_index(2)]
-		#[pallet::weight(<T as Config>::WeightInfo::unstake())]
-		pub fn unstake_all(staker: OriginFor<T>) -> DispatchResultWithPostInfo {
+		#[pallet::weight(<T as Config>::WeightInfo::unstake_all())]
+		pub fn unstake_all(staker: OriginFor<T>) -> DispatchResult {
 			let staker_id = ensure_signed(staker)?;
-			let config = <PalletConfiguration<T>>::get();
-
-			// calculate block number where the sum would be free
-			let block = <frame_system::Pallet<T>>::block_number() + config.pending_interval;
-
-			let mut pendings = <PendingUnstake<T>>::get(block);
-
-			// checks that we can do unreserve stakes in the block
-			ensure!(!pendings.is_full(), Error::<T>::PendingForBlockOverflow);
-
-			let mut total_stakes = 0u64;
-
-			let total_staked: BalanceOf<T> = Staked::<T>::drain_prefix((&staker_id,))
-				.map(|(_, (amount, _))| {
-					total_stakes += 1;
-					amount
-				})
-				.sum();
-
-			if total_staked.is_zero() {
-				return Ok(None::<Weight>.into()); // TO-DO
-			}
-
-			pendings
-				.try_push((staker_id.clone(), total_staked))
-				.map_err(|_| Error::<T>::PendingForBlockOverflow)?;
 
-			<PendingUnstake<T>>::insert(block, pendings);
-
-			TotalStaked::<T>::set(
-				TotalStaked::<T>::get()
-					.checked_sub(&total_staked)
-					.ok_or(ArithmeticError::Underflow)?,
-			);
-
-			StakesPerAccount::<T>::remove(&staker_id);
-
-			Self::deposit_event(Event::Unstake(staker_id, total_staked));
-
-			Ok(None::<Weight>.into())
+			Self::unstake_all_internal(staker_id)
 		}
 
-		/// Unstakes all stakes.
-		/// Moves the sum of all stakes to the `reserved` state.
+		/// Unstakes the amount of balance for the staker.
 		/// After the end of `PendingInterval` this sum becomes completely
 		/// free for further use.
+		///
+		///  # Arguments
+		///
+		/// * `staker`: staker account.
+		/// * `amount`: amount of unstaked funds.
 		#[pallet::call_index(8)]
-		#[pallet::weight(<T as Config>::WeightInfo::unstake())]
+		#[pallet::weight(<T as Config>::WeightInfo::unstake_partial())]
 		pub fn unstake_partial(staker: OriginFor<T>, amount: BalanceOf<T>) -> DispatchResult {
 			let staker_id = ensure_signed(staker)?;
 
-			Self::partial_unstake(&staker_id, amount)
+			Self::unstake_partial_internal(staker_id, amount)
 		}
 
 		/// Sets the pallet to be the sponsor for the collection.
@@ -821,12 +788,18 @@
 		T::PalletId::get().into_account_truncating()
 	}
 
-	fn partial_unstake(staker_id: &T::AccountId, unstaked_balance: BalanceOf<T>) -> DispatchResult {
-		
+	/// Unstakes the balance for the staker.
+	///
+	/// - `staker`: staker account.
+	/// - `amount`: amount of unstaked funds.
+	fn unstake_partial_internal(
+		staker_id: T::AccountId,
+		unstaked_balance: BalanceOf<T>,
+	) -> DispatchResult {
 		if unstaked_balance == Default::default() {
 			return Ok(());
 		}
-		
+
 		let config = <PalletConfiguration<T>>::get();
 
 		// calculate block number where the sum would be free
@@ -837,7 +810,7 @@
 		// checks that we can do unreserve stakes in the block
 		ensure!(!pendings.is_full(), Error::<T>::PendingForBlockOverflow);
 
-		let mut stakes = Staked::<T>::iter_prefix((staker_id,)).collect::<Vec<_>>();
+		let mut stakes = Staked::<T>::iter_prefix((&staker_id,)).collect::<Vec<_>>();
 
 		let total_staked = stakes
 			.iter()
@@ -845,7 +818,10 @@
 				acc + *balance
 			});
 
-		ensure!(total_staked >= unstaked_balance, ArithmeticError::Underflow);
+		ensure!(
+			total_staked >= unstaked_balance,
+			<Error<T>>::InsufficientStakedBalance
+		);
 
 		<TotalStaked<T>>::set(
 			<TotalStaked<T>>::get()
@@ -880,7 +856,7 @@
 			.try_push((staker_id.clone(), unstaked_balance))
 			.map_err(|_| Error::<T>::PendingForBlockOverflow)?;
 
-		StakesPerAccount::<T>::try_mutate(staker_id, |stakes| -> DispatchResult {
+		StakesPerAccount::<T>::try_mutate(&staker_id, |stakes| -> DispatchResult {
 			*stakes = stakes
 				.checked_sub(will_deleted_stakes_count)
 				.ok_or(ArithmeticError::Underflow)?;
@@ -891,9 +867,9 @@
 			.iter()
 			.for_each(|(staked_block, (current_stake_state, _))| {
 				if current_stake_state == &Default::default() {
-					<Staked<T>>::remove((staker_id, staked_block));
+					<Staked<T>>::remove((&staker_id, staked_block));
 				} else {
-					<Staked<T>>::mutate((staker_id, staked_block), |(old_stake_state, _)| {
+					<Staked<T>>::mutate((&staker_id, staked_block), |(old_stake_state, _)| {
 						*old_stake_state = *current_stake_state
 					});
 				}
@@ -901,7 +877,7 @@
 
 		<PendingUnstake<T>>::insert(unpending_block, pendings);
 
-		Self::deposit_event(Event::Unstake(staker_id.clone(), total_staked));
+		Self::deposit_event(Event::Unstake(staker_id, total_staked));
 
 		Ok(())
 	}
@@ -1102,4 +1078,47 @@
 		unsorted_res.sort_by_key(|(block, _)| *block);
 		unsorted_res
 	}
+
+	fn unstake_all_internal(staker_id: T::AccountId) -> DispatchResult {
+		let config = <PalletConfiguration<T>>::get();
+
+		// calculate block number where the sum would be free
+		let block = <frame_system::Pallet<T>>::block_number() + config.pending_interval;
+
+		let mut pendings = <PendingUnstake<T>>::get(block);
+
+		// checks that we can do unreserve stakes in the block
+		ensure!(!pendings.is_full(), Error::<T>::PendingForBlockOverflow);
+
+		let mut total_stakes = 0u64;
+
+		let total_staked: BalanceOf<T> = Staked::<T>::drain_prefix((&staker_id,))
+			.map(|(_, (amount, _))| {
+				total_stakes += 1;
+				amount
+			})
+			.sum();
+
+		if total_staked.is_zero() {
+			return Ok(());
+		}
+
+		pendings
+			.try_push((staker_id.clone(), total_staked))
+			.map_err(|_| Error::<T>::PendingForBlockOverflow)?;
+
+		<PendingUnstake<T>>::insert(block, pendings);
+
+		TotalStaked::<T>::set(
+			TotalStaked::<T>::get()
+				.checked_sub(&total_staked)
+				.ok_or(ArithmeticError::Underflow)?,
+		);
+
+		StakesPerAccount::<T>::remove(&staker_id);
+
+		Self::deposit_event(Event::Unstake(staker_id, total_staked));
+
+		Ok(())
+	}
 }
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-12-25, STEPS: `50`, REPEAT: 80, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! DATE: 2023-02-14, STEPS: `50`, REPEAT: 80, LOW RANGE: `[]`, HIGH RANGE: `[]`
 //! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024
 
 // Executed Command:
@@ -38,7 +38,8 @@
 	fn set_admin_address() -> Weight;
 	fn payout_stakers(b: u32, ) -> Weight;
 	fn stake() -> Weight;
-	fn unstake() -> Weight;
+	fn unstake_all() -> Weight;
+	fn unstake_partial() -> Weight;
 	fn sponsor_collection() -> Weight;
 	fn stop_sponsoring_collection() -> Weight;
 	fn sponsor_contract() -> Weight;
@@ -49,18 +50,19 @@
 pub struct SubstrateWeight<T>(PhantomData<T>);
 impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	// Storage: AppPromotion PendingUnstake (r:1 w:0)
+	// Storage: Balances Locks (r:1 w:1)
 	// Storage: System Account (r:1 w:1)
 	fn on_initialize(b: u32, ) -> Weight {
-		Weight::from_ref_time(3_079_948 as u64)
-			// Standard Error: 30_376
-			.saturating_add(Weight::from_ref_time(6_343_630 as u64).saturating_mul(b as u64))
+		Weight::from_ref_time(2_562_275 as u64)
+			// Standard Error: 21_950
+			.saturating_add(Weight::from_ref_time(7_177_129 as u64).saturating_mul(b as u64))
 			.saturating_add(T::DbWeight::get().reads(1 as u64))
-			.saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(b as u64)))
-			.saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(b as u64)))
+			.saturating_add(T::DbWeight::get().reads((2 as u64).saturating_mul(b as u64)))
+			.saturating_add(T::DbWeight::get().writes((2 as u64).saturating_mul(b as u64)))
 	}
 	// Storage: AppPromotion Admin (r:0 w:1)
 	fn set_admin_address() -> Weight {
-		Weight::from_ref_time(6_653_000 as u64)
+		Weight::from_ref_time(6_146_000 as u64)
 			.saturating_add(T::DbWeight::get().writes(1 as u64))
 	}
 	// Storage: AppPromotion Admin (r:1 w:0)
@@ -72,9 +74,9 @@
 	// Storage: Balances Locks (r:1 w:1)
 	// Storage: AppPromotion TotalStaked (r:1 w:1)
 	fn payout_stakers(b: u32, ) -> Weight {
-		Weight::from_ref_time(74_048_000 as u64)
-			// Standard Error: 33_223
-			.saturating_add(Weight::from_ref_time(57_702_092 as u64).saturating_mul(b as u64))
+		Weight::from_ref_time(64_035_000 as u64)
+			// Standard Error: 19_434
+			.saturating_add(Weight::from_ref_time(47_251_111 as u64).saturating_mul(b as u64))
 			.saturating_add(T::DbWeight::get().reads(7 as u64))
 			.saturating_add(T::DbWeight::get().reads((12 as u64).saturating_mul(b as u64)))
 			.saturating_add(T::DbWeight::get().writes(3 as u64))
@@ -88,47 +90,55 @@
 	// Storage: AppPromotion Staked (r:1 w:1)
 	// Storage: AppPromotion TotalStaked (r:1 w:1)
 	fn stake() -> Weight {
-		Weight::from_ref_time(20_314_000 as u64)
+		Weight::from_ref_time(18_078_000 as u64)
 			.saturating_add(T::DbWeight::get().reads(7 as u64))
 			.saturating_add(T::DbWeight::get().writes(5 as u64))
 	}
 	// Storage: Configuration AppPromomotionConfigurationOverride (r:1 w:0)
 	// Storage: AppPromotion PendingUnstake (r:1 w:1)
 	// Storage: AppPromotion Staked (r:11 w:10)
-	// Storage: Balances Locks (r:1 w:1)
-	// Storage: System Account (r:1 w:1)
 	// Storage: AppPromotion TotalStaked (r:1 w:1)
 	// Storage: AppPromotion StakesPerAccount (r:0 w:1)
-	fn unstake() -> Weight {
-		Weight::from_ref_time(64_582_000 as u64)
-			.saturating_add(T::DbWeight::get().reads(16 as u64))
-			.saturating_add(T::DbWeight::get().writes(15 as u64))
+	fn unstake_all() -> Weight {
+		Weight::from_ref_time(45_038_000 as u64)
+			.saturating_add(T::DbWeight::get().reads(14 as u64))
+			.saturating_add(T::DbWeight::get().writes(13 as u64))
+	}
+	// Storage: Configuration AppPromomotionConfigurationOverride (r:1 w:0)
+	// Storage: AppPromotion PendingUnstake (r:1 w:1)
+	// Storage: AppPromotion Staked (r:11 w:10)
+	// Storage: AppPromotion TotalStaked (r:1 w:1)
+	// Storage: AppPromotion StakesPerAccount (r:1 w:1)
+	fn unstake_partial() -> Weight {
+		Weight::from_ref_time(48_863_000 as u64)
+			.saturating_add(T::DbWeight::get().reads(15 as u64))
+			.saturating_add(T::DbWeight::get().writes(13 as u64))
 	}
 	// Storage: AppPromotion Admin (r:1 w:0)
 	// Storage: Common CollectionById (r:1 w:1)
 	fn sponsor_collection() -> Weight {
-		Weight::from_ref_time(16_364_000 as u64)
+		Weight::from_ref_time(14_808_000 as u64)
 			.saturating_add(T::DbWeight::get().reads(2 as u64))
 			.saturating_add(T::DbWeight::get().writes(1 as u64))
 	}
 	// Storage: AppPromotion Admin (r:1 w:0)
 	// Storage: Common CollectionById (r:1 w:1)
 	fn stop_sponsoring_collection() -> Weight {
-		Weight::from_ref_time(15_710_000 as u64)
+		Weight::from_ref_time(14_587_000 as u64)
 			.saturating_add(T::DbWeight::get().reads(2 as u64))
 			.saturating_add(T::DbWeight::get().writes(1 as u64))
 	}
 	// Storage: AppPromotion Admin (r:1 w:0)
 	// Storage: EvmContractHelpers Sponsoring (r:0 w:1)
 	fn sponsor_contract() -> Weight {
-		Weight::from_ref_time(12_669_000 as u64)
+		Weight::from_ref_time(11_791_000 as u64)
 			.saturating_add(T::DbWeight::get().reads(1 as u64))
 			.saturating_add(T::DbWeight::get().writes(1 as u64))
 	}
 	// Storage: AppPromotion Admin (r:1 w:0)
 	// Storage: EvmContractHelpers Sponsoring (r:1 w:1)
 	fn stop_sponsoring_contract() -> Weight {
-		Weight::from_ref_time(14_406_000 as u64)
+		Weight::from_ref_time(13_576_000 as u64)
 			.saturating_add(T::DbWeight::get().reads(2 as u64))
 			.saturating_add(T::DbWeight::get().writes(1 as u64))
 	}
@@ -137,18 +147,19 @@
 // For backwards compatibility and tests
 impl WeightInfo for () {
 	// Storage: AppPromotion PendingUnstake (r:1 w:0)
+	// Storage: Balances Locks (r:1 w:1)
 	// Storage: System Account (r:1 w:1)
 	fn on_initialize(b: u32, ) -> Weight {
-		Weight::from_ref_time(3_079_948 as u64)
-			// Standard Error: 30_376
-			.saturating_add(Weight::from_ref_time(6_343_630 as u64).saturating_mul(b as u64))
+		Weight::from_ref_time(2_562_275 as u64)
+			// Standard Error: 21_950
+			.saturating_add(Weight::from_ref_time(7_177_129 as u64).saturating_mul(b as u64))
 			.saturating_add(RocksDbWeight::get().reads(1 as u64))
-			.saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(b as u64)))
-			.saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(b as u64)))
+			.saturating_add(RocksDbWeight::get().reads((2 as u64).saturating_mul(b as u64)))
+			.saturating_add(RocksDbWeight::get().writes((2 as u64).saturating_mul(b as u64)))
 	}
 	// Storage: AppPromotion Admin (r:0 w:1)
 	fn set_admin_address() -> Weight {
-		Weight::from_ref_time(6_653_000 as u64)
+		Weight::from_ref_time(6_146_000 as u64)
 			.saturating_add(RocksDbWeight::get().writes(1 as u64))
 	}
 	// Storage: AppPromotion Admin (r:1 w:0)
@@ -160,9 +171,9 @@
 	// Storage: Balances Locks (r:1 w:1)
 	// Storage: AppPromotion TotalStaked (r:1 w:1)
 	fn payout_stakers(b: u32, ) -> Weight {
-		Weight::from_ref_time(74_048_000 as u64)
-			// Standard Error: 33_223
-			.saturating_add(Weight::from_ref_time(57_702_092 as u64).saturating_mul(b as u64))
+		Weight::from_ref_time(64_035_000 as u64)
+			// Standard Error: 19_434
+			.saturating_add(Weight::from_ref_time(47_251_111 as u64).saturating_mul(b as u64))
 			.saturating_add(RocksDbWeight::get().reads(7 as u64))
 			.saturating_add(RocksDbWeight::get().reads((12 as u64).saturating_mul(b as u64)))
 			.saturating_add(RocksDbWeight::get().writes(3 as u64))
@@ -176,47 +187,55 @@
 	// Storage: AppPromotion Staked (r:1 w:1)
 	// Storage: AppPromotion TotalStaked (r:1 w:1)
 	fn stake() -> Weight {
-		Weight::from_ref_time(20_314_000 as u64)
+		Weight::from_ref_time(18_078_000 as u64)
 			.saturating_add(RocksDbWeight::get().reads(7 as u64))
 			.saturating_add(RocksDbWeight::get().writes(5 as u64))
 	}
 	// Storage: Configuration AppPromomotionConfigurationOverride (r:1 w:0)
 	// Storage: AppPromotion PendingUnstake (r:1 w:1)
 	// Storage: AppPromotion Staked (r:11 w:10)
-	// Storage: Balances Locks (r:1 w:1)
-	// Storage: System Account (r:1 w:1)
 	// Storage: AppPromotion TotalStaked (r:1 w:1)
 	// Storage: AppPromotion StakesPerAccount (r:0 w:1)
-	fn unstake() -> Weight {
-		Weight::from_ref_time(64_582_000 as u64)
-			.saturating_add(RocksDbWeight::get().reads(16 as u64))
-			.saturating_add(RocksDbWeight::get().writes(15 as u64))
+	fn unstake_all() -> Weight {
+		Weight::from_ref_time(45_038_000 as u64)
+			.saturating_add(RocksDbWeight::get().reads(14 as u64))
+			.saturating_add(RocksDbWeight::get().writes(13 as u64))
 	}
+	// Storage: Configuration AppPromomotionConfigurationOverride (r:1 w:0)
+	// Storage: AppPromotion PendingUnstake (r:1 w:1)
+	// Storage: AppPromotion Staked (r:11 w:10)
+	// Storage: AppPromotion TotalStaked (r:1 w:1)
+	// Storage: AppPromotion StakesPerAccount (r:1 w:1)
+	fn unstake_partial() -> Weight {
+		Weight::from_ref_time(48_863_000 as u64)
+			.saturating_add(RocksDbWeight::get().reads(15 as u64))
+			.saturating_add(RocksDbWeight::get().writes(13 as u64))
+	}
 	// Storage: AppPromotion Admin (r:1 w:0)
 	// Storage: Common CollectionById (r:1 w:1)
 	fn sponsor_collection() -> Weight {
-		Weight::from_ref_time(16_364_000 as u64)
+		Weight::from_ref_time(14_808_000 as u64)
 			.saturating_add(RocksDbWeight::get().reads(2 as u64))
 			.saturating_add(RocksDbWeight::get().writes(1 as u64))
 	}
 	// Storage: AppPromotion Admin (r:1 w:0)
 	// Storage: Common CollectionById (r:1 w:1)
 	fn stop_sponsoring_collection() -> Weight {
-		Weight::from_ref_time(15_710_000 as u64)
+		Weight::from_ref_time(14_587_000 as u64)
 			.saturating_add(RocksDbWeight::get().reads(2 as u64))
 			.saturating_add(RocksDbWeight::get().writes(1 as u64))
 	}
 	// Storage: AppPromotion Admin (r:1 w:0)
 	// Storage: EvmContractHelpers Sponsoring (r:0 w:1)
 	fn sponsor_contract() -> Weight {
-		Weight::from_ref_time(12_669_000 as u64)
+		Weight::from_ref_time(11_791_000 as u64)
 			.saturating_add(RocksDbWeight::get().reads(1 as u64))
 			.saturating_add(RocksDbWeight::get().writes(1 as u64))
 	}
 	// Storage: AppPromotion Admin (r:1 w:0)
 	// Storage: EvmContractHelpers Sponsoring (r:1 w:1)
 	fn stop_sponsoring_contract() -> Weight {
-		Weight::from_ref_time(14_406_000 as u64)
+		Weight::from_ref_time(13_576_000 as u64)
 			.saturating_add(RocksDbWeight::get().reads(2 as u64))
 			.saturating_add(RocksDbWeight::get().writes(1 as u64))
 	}
modifiedtests/src/sub/appPromotion/appPromotion.test.tsdiffbeforeafterboth
--- a/tests/src/sub/appPromotion/appPromotion.test.ts
+++ b/tests/src/sub/appPromotion/appPromotion.test.ts
@@ -247,7 +247,7 @@
         // unstake has no effect if no stakes at all
         testCase.method === 'unstakeAll'
           ? await helper.staking.unstakeAll(staker)
-          : await expect(helper.staking.unstakePartial(staker, 100n * nominal)).to.be.rejectedWith('Arithmetic: Underflow');
+          : await expect(helper.staking.unstakePartial(staker, 100n * nominal)).to.be.rejectedWith('appPromotion.InsufficientStakedBalance');
 
         expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(0n);
         expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n); // TODO bigint closeTo helper
@@ -262,7 +262,7 @@
           await helper.staking.unstakeAll(staker);
         } else {
           await helper.staking.unstakePartial(staker, 100n * nominal);
-          await expect(helper.staking.unstakePartial(staker, 100n * nominal)).to.be.rejectedWith('Arithmetic: Underflow');
+          await expect(helper.staking.unstakePartial(staker, 100n * nominal)).to.be.rejectedWith('appPromotion.InsufficientStakedBalance');
         }
 
         expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(0);