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
--- a/pallets/app-promotion/src/benchmarking.rs
+++ b/pallets/app-promotion/src/benchmarking.rs
@@ -65,7 +65,7 @@
 			let staker = account::<T::AccountId>("staker", index, SEED);
 			<T as Config>::Currency::make_free_balance_be(&staker,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());
 			PromototionPallet::<T>::stake(RawOrigin::Signed(staker.clone()).into(), Into::<BalanceOf<T>>::into(100u128) * T::Nominal::get())?;
-			PromototionPallet::<T>::unstake(RawOrigin::Signed(staker.clone()).into()).map_err(|e| e.error)?;
+			PromototionPallet::<T>::unstake_all(RawOrigin::Signed(staker.clone()).into()).map_err(|e| e.error)?;
 			Result::<(), sp_runtime::DispatchError>::Ok(())
 		})?;
 		let block_number = <frame_system::Pallet<T>>::current_block_number() + T::PendingInterval::get();
@@ -115,7 +115,7 @@
 		let _ = <T as Config>::Currency::make_free_balance_be(&caller,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());
 	} : _(RawOrigin::Signed(caller.clone()), share * <T as Config>::Currency::total_balance(&caller))
 
-	unstake {
+	unstake_all {
 		let caller = account::<T::AccountId>("caller", 0, SEED);
 		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());
@@ -130,6 +130,21 @@
 
 	} : _(RawOrigin::Signed(caller.clone()))
 
+	unstake_partial {
+		let caller = account::<T::AccountId>("caller", 0, SEED);
+		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());
+		(1..11).map(|i| {
+			// used to change block number
+			<frame_system::Pallet<T>>::set_block_number(i.into());
+			T::RelayBlockNumberProvider::set_block_number((2*i).into());
+			assert_eq!(<frame_system::Pallet<T>>::block_number(), i.into());
+			assert_eq!(T::RelayBlockNumberProvider::current_block_number(), (2*i).into());
+			PromototionPallet::<T>::stake(RawOrigin::Signed(caller.clone()).into(), Into::<BalanceOf<T>>::into(100u128) * T::Nominal::get())
+		}).collect::<Result<Vec<_>, _>>()?;
+
+	} : _(RawOrigin::Signed(caller.clone()), Into::<BalanceOf<T>>::into(1000u128) * T::Nominal::get())
+
 	sponsor_collection {
 		let pallet_admin = account::<T::AccountId>("admin", 0, SEED);
 		PromototionPallet::<T>::set_admin_address(RawOrigin::Root.into(), T::CrossAccountId::from_sub(pallet_admin.clone()))?;
modifiedpallets/app-promotion/src/lib.rsdiffbeforeafterboth
208 SponsorNotSet,208 SponsorNotSet,
209 /// Errors caused by incorrect actions with a locked balance.209 /// Errors caused by incorrect actions with a locked balance.
210 IncorrectLockedBalanceOperation,210 IncorrectLockedBalanceOperation,
211 /// Errors caused by insufficient staked balance.
212 InsufficientStakedBalance,
211 }213 }
212214
213 /// Stores the total staked amount.215 /// Stores the total staked amount.
489 }491 }
490492
491 /// Unstakes all stakes.493 /// Unstakes all stakes.
492 /// Moves the sum of all stakes to the `reserved` state.
493 /// After the end of `PendingInterval` this sum becomes completely494 /// After the end of `PendingInterval` this sum becomes completely
494 /// free for further use.495 /// free for further use.
495 #[pallet::call_index(2)]496 #[pallet::call_index(2)]
496 #[pallet::weight(<T as Config>::WeightInfo::unstake())]497 #[pallet::weight(<T as Config>::WeightInfo::unstake_all())]
497 pub fn unstake_all(staker: OriginFor<T>) -> DispatchResultWithPostInfo {498 pub fn unstake_all(staker: OriginFor<T>) -> DispatchResult {
498 let staker_id = ensure_signed(staker)?;499 let staker_id = ensure_signed(staker)?;
499 let config = <PalletConfiguration<T>>::get();
500
501 // calculate block number where the sum would be free
502 let block = <frame_system::Pallet<T>>::block_number() + config.pending_interval;
503
504 let mut pendings = <PendingUnstake<T>>::get(block);
505
506 // checks that we can do unreserve stakes in the block
507 ensure!(!pendings.is_full(), Error::<T>::PendingForBlockOverflow);
508
509 let mut total_stakes = 0u64;
510
511 let total_staked: BalanceOf<T> = Staked::<T>::drain_prefix((&staker_id,))
512 .map(|(_, (amount, _))| {
513 total_stakes += 1;
514 amount
515 })
516 .sum();
517
518 if total_staked.is_zero() {
519 return Ok(None::<Weight>.into()); // TO-DO
520 }
521
522 pendings
523 .try_push((staker_id.clone(), total_staked))
524 .map_err(|_| Error::<T>::PendingForBlockOverflow)?;
525
526 <PendingUnstake<T>>::insert(block, pendings);
527
528 TotalStaked::<T>::set(
529 TotalStaked::<T>::get()
530 .checked_sub(&total_staked)
531 .ok_or(ArithmeticError::Underflow)?,
532 );
533
534 StakesPerAccount::<T>::remove(&staker_id);
535500
536 Self::deposit_event(Event::Unstake(staker_id, total_staked));501 Self::unstake_all_internal(staker_id)
537
538 Ok(None::<Weight>.into())
539 }502 }
540503
541 /// Unstakes all stakes.504 /// Unstakes the amount of balance for the staker.
542 /// Moves the sum of all stakes to the `reserved` state.
543 /// After the end of `PendingInterval` this sum becomes completely505 /// After the end of `PendingInterval` this sum becomes completely
544 /// free for further use.506 /// free for further use.
507 ///
508 /// # Arguments
509 ///
510 /// * `staker`: staker account.
511 /// * `amount`: amount of unstaked funds.
545 #[pallet::call_index(8)]512 #[pallet::call_index(8)]
546 #[pallet::weight(<T as Config>::WeightInfo::unstake())]513 #[pallet::weight(<T as Config>::WeightInfo::unstake_partial())]
547 pub fn unstake_partial(staker: OriginFor<T>, amount: BalanceOf<T>) -> DispatchResult {514 pub fn unstake_partial(staker: OriginFor<T>, amount: BalanceOf<T>) -> DispatchResult {
548 let staker_id = ensure_signed(staker)?;515 let staker_id = ensure_signed(staker)?;
549516
550 Self::partial_unstake(&staker_id, amount)517 Self::unstake_partial_internal(staker_id, amount)
551 }518 }
552519
553 /// Sets the pallet to be the sponsor for the collection.520 /// Sets the pallet to be the sponsor for the collection.
821 T::PalletId::get().into_account_truncating()788 T::PalletId::get().into_account_truncating()
822 }789 }
823790
791 /// Unstakes the balance for the staker.
792 ///
793 /// - `staker`: staker account.
794 /// - `amount`: amount of unstaked funds.
824 fn partial_unstake(staker_id: &T::AccountId, unstaked_balance: BalanceOf<T>) -> DispatchResult {795 fn unstake_partial_internal(
825 796 staker_id: T::AccountId,
797 unstaked_balance: BalanceOf<T>,
798 ) -> DispatchResult {
826 if unstaked_balance == Default::default() {799 if unstaked_balance == Default::default() {
827 return Ok(());800 return Ok(());
837 // checks that we can do unreserve stakes in the block810 // checks that we can do unreserve stakes in the block
838 ensure!(!pendings.is_full(), Error::<T>::PendingForBlockOverflow);811 ensure!(!pendings.is_full(), Error::<T>::PendingForBlockOverflow);
839812
840 let mut stakes = Staked::<T>::iter_prefix((staker_id,)).collect::<Vec<_>>();813 let mut stakes = Staked::<T>::iter_prefix((&staker_id,)).collect::<Vec<_>>();
841814
842 let total_staked = stakes815 let total_staked = stakes
843 .iter()816 .iter()
844 .fold(<BalanceOf<T>>::default(), |acc, (_, (balance, _))| {817 .fold(<BalanceOf<T>>::default(), |acc, (_, (balance, _))| {
845 acc + *balance818 acc + *balance
846 });819 });
847820
848 ensure!(total_staked >= unstaked_balance, ArithmeticError::Underflow);821 ensure!(
822 total_staked >= unstaked_balance,
823 <Error<T>>::InsufficientStakedBalance
824 );
849825
850 <TotalStaked<T>>::set(826 <TotalStaked<T>>::set(
880 .try_push((staker_id.clone(), unstaked_balance))856 .try_push((staker_id.clone(), unstaked_balance))
881 .map_err(|_| Error::<T>::PendingForBlockOverflow)?;857 .map_err(|_| Error::<T>::PendingForBlockOverflow)?;
882858
883 StakesPerAccount::<T>::try_mutate(staker_id, |stakes| -> DispatchResult {859 StakesPerAccount::<T>::try_mutate(&staker_id, |stakes| -> DispatchResult {
884 *stakes = stakes860 *stakes = stakes
885 .checked_sub(will_deleted_stakes_count)861 .checked_sub(will_deleted_stakes_count)
886 .ok_or(ArithmeticError::Underflow)?;862 .ok_or(ArithmeticError::Underflow)?;
891 .iter()867 .iter()
892 .for_each(|(staked_block, (current_stake_state, _))| {868 .for_each(|(staked_block, (current_stake_state, _))| {
893 if current_stake_state == &Default::default() {869 if current_stake_state == &Default::default() {
894 <Staked<T>>::remove((staker_id, staked_block));870 <Staked<T>>::remove((&staker_id, staked_block));
895 } else {871 } else {
896 <Staked<T>>::mutate((staker_id, staked_block), |(old_stake_state, _)| {872 <Staked<T>>::mutate((&staker_id, staked_block), |(old_stake_state, _)| {
897 *old_stake_state = *current_stake_state873 *old_stake_state = *current_stake_state
898 });874 });
899 }875 }
900 });876 });
901877
902 <PendingUnstake<T>>::insert(unpending_block, pendings);878 <PendingUnstake<T>>::insert(unpending_block, pendings);
903879
904 Self::deposit_event(Event::Unstake(staker_id.clone(), total_staked));880 Self::deposit_event(Event::Unstake(staker_id, total_staked));
905881
906 Ok(())882 Ok(())
907 }883 }
1103 unsorted_res1079 unsorted_res
1104 }1080 }
1081
1082 fn unstake_all_internal(staker_id: T::AccountId) -> DispatchResult {
1083 let config = <PalletConfiguration<T>>::get();
1084
1085 // calculate block number where the sum would be free
1086 let block = <frame_system::Pallet<T>>::block_number() + config.pending_interval;
1087
1088 let mut pendings = <PendingUnstake<T>>::get(block);
1089
1090 // checks that we can do unreserve stakes in the block
1091 ensure!(!pendings.is_full(), Error::<T>::PendingForBlockOverflow);
1092
1093 let mut total_stakes = 0u64;
1094
1095 let total_staked: BalanceOf<T> = Staked::<T>::drain_prefix((&staker_id,))
1096 .map(|(_, (amount, _))| {
1097 total_stakes += 1;
1098 amount
1099 })
1100 .sum();
1101
1102 if total_staked.is_zero() {
1103 return Ok(());
1104 }
1105
1106 pendings
1107 .try_push((staker_id.clone(), total_staked))
1108 .map_err(|_| Error::<T>::PendingForBlockOverflow)?;
1109
1110 <PendingUnstake<T>>::insert(block, pendings);
1111
1112 TotalStaked::<T>::set(
1113 TotalStaked::<T>::get()
1114 .checked_sub(&total_staked)
1115 .ok_or(ArithmeticError::Underflow)?,
1116 );
1117
1118 StakesPerAccount::<T>::remove(&staker_id);
1119
1120 Self::deposit_event(Event::Unstake(staker_id, total_staked));
1121
1122 Ok(())
1123 }
1105}1124}
11061125
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);