1#![cfg_attr(not(feature = "std"), no_std)]
232
233#[cfg(feature = "runtime-benchmarks")]
238mod benchmarking;
239#[cfg(test)]
240mod mock;
241#[cfg(test)]
242mod tests;
243mod xp;
244mod fungible;
245pub mod types;
246pub mod weights;
247
248pub use pallet::*;
253
254#[frame_support::pallet]
255pub mod pallet {
256
257 use core::fmt::Debug;
263
264 use crate::{
266 types::{
267 ForceGenesisConfig, GenesisAcc, IdXp, Stepper, Xp, XpEligibility, XpId, XpProgress,
268 XpState,
269 },
270 weights::WeightInfo,
271 };
272
273 use frame_suite::{
275 accumulators::DiscreteAccumulator,
276 base::{Asset, Delimited, RuntimeEnum, Time},
277 xp::{
278 XpLockListener, XpMutate, XpMutateListener, XpOwner, XpOwnerListener, XpReap,
279 XpReapListener, XpReserveListener, XpSystem, XpSystemExtensions,
280 },
281 };
282
283 use frame_support::{
285 dispatch::{DispatchResult, GetDispatchInfo},
286 pallet_prelude::*,
287 traits::{IsSubType, VariantCount, VariantCountOf},
288 Blake2_128Concat,
289 };
290
291 use frame_system::{
293 ensure_root,
294 pallet_prelude::{BlockNumberFor, *},
295 };
296
297 use scale_info::prelude::boxed::Box;
299
300 use sp_runtime::{traits::Dispatchable, DispatchError, Vec};
302
303 #[pallet::pallet]
343 pub struct Pallet<T, I = ()>(PhantomData<(T, I)>);
344
345 #[pallet::config]
362 pub trait Config<I: 'static = ()>: frame_system::Config {
363 type RuntimeEvent: From<Event<Self, I>>
367 + IsType<<Self as frame_system::Config>::RuntimeEvent>;
368
369 type RuntimeCall: Parameter
371 + Dispatchable<RuntimeOrigin = Self::RuntimeOrigin>
372 + GetDispatchInfo
373 + From<frame_system::Call<Self>>
374 + IsSubType<Call<Self, I>>
375 + IsType<<Self as frame_system::Config>::RuntimeCall>;
376
377 type ReserveReason: RuntimeEnum + Delimited + Copy + VariantCount;
383
384 type LockReason: RuntimeEnum + Delimited + Copy + VariantCount;
390
391 type Xp: Asset + From<Self::Pulse>;
395
396 type Pulse: Time;
399
400 type WeightInfo: WeightInfo;
404
405 type Extensions: XpSystemExtensions<Via = Pallet<Self, I>>
416 + XpOwnerListener
417 + XpMutateListener
418 + XpReserveListener
419 + XpLockListener
420 + XpReapListener;
421
422 #[pallet::constant]
431 type EmitEvents: Get<bool> + Clone + Debug;
432 }
433
434 #[pallet::genesis_config]
443 pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
444 pub min_pulse: T::Pulse,
449
450 pub init_xp: T::Xp,
455
456 pub pulse_factor: Stepper<T, I>,
461
462 pub genesis_acc: Vec<GenesisAcc<T::AccountId, XpId<T>>>,
467 }
468
469 impl<T: Config<I>, I: 'static> Default for GenesisConfig<T, I> {
471 fn default() -> Self {
472 Self {
473 min_pulse: 3u32.into(),
474 init_xp: 1u32.into(),
475 pulse_factor: Stepper::<T, I>::new(50u8.into(), 10u8.into()).unwrap(),
476 genesis_acc: Vec::new(),
477 }
478 }
479 }
480
481 #[pallet::genesis_build]
483 impl<T: Config<I>, I: 'static> BuildGenesisConfig for GenesisConfig<T, I> {
484 fn build(&self) {
485 MinPulse::<T, I>::put(self.min_pulse);
486 InitXp::<T, I>::put(self.init_xp);
487 MinTimeStamp::<T, I>::put(BlockNumberFor::<T>::zero());
488 PulseFactor::<T, I>::put(&self.pulse_factor);
489
490 for acc_struct in &self.genesis_acc {
491 Pallet::<T, I>::new_xp(&acc_struct.owner, &acc_struct.id)
492 }
493 }
494 }
495
496 #[pallet::storage]
505 pub type XpOf<T: Config<I>, I: 'static = ()> =
506 StorageMap<_, Blake2_128Concat, XpId<T>, Xp<T, I>, OptionQuery>;
507
508 #[pallet::storage]
514 pub type XpOwners<T: Config<I>, I: 'static = ()> = StorageNMap<
515 _,
516 (
517 NMapKey<Blake2_128Concat, T::AccountId>,
518 NMapKey<Blake2_128Concat, XpId<T>>,
519 ),
520 (),
521 OptionQuery,
522 >;
523
524 #[pallet::storage]
533 pub type ReservedXpOf<T: Config<I>, I: 'static = ()> = StorageMap<
534 _,
535 Blake2_128Concat,
536 XpId<T>,
537 BoundedVec<IdXp<T::ReserveReason, T::Xp>, VariantCountOf<T::ReserveReason>>,
538 OptionQuery,
539 >;
540
541 #[pallet::storage]
550 pub type LockedXpOf<T: Config<I>, I: 'static = ()> = StorageMap<
551 _,
552 Blake2_128Concat,
553 XpId<T>,
554 BoundedVec<IdXp<T::LockReason, T::Xp>, VariantCountOf<T::LockReason>>,
555 OptionQuery,
556 >;
557
558 #[pallet::storage]
563 pub type ReapedXp<T: Config<I>, I: 'static = ()> =
564 StorageMap<_, Blake2_128Concat, XpId<T>, (), OptionQuery>;
565
566 #[pallet::storage]
572 pub type MinPulse<T: Config<I>, I: 'static = ()> = StorageValue<_, T::Pulse, ValueQuery>;
573
574 #[pallet::storage]
579 pub type InitXp<T: Config<I>, I: 'static = ()> = StorageValue<_, T::Xp, ValueQuery>;
580
581 #[pallet::storage]
586 pub type PulseFactor<T: Config<I>, I: 'static = ()> =
587 StorageValue<_, Stepper<T, I>, ValueQuery>;
588
589 #[pallet::storage]
595 pub type MinTimeStamp<T: Config<I>, I: 'static = ()> =
596 StorageValue<_, BlockNumberFor<T>, ValueQuery>;
597
598 #[pallet::error]
603 pub enum Error<T, I = ()> {
605 XpNotFound,
607 XpNotDead,
609 InvalidXpOwner,
611 AlreadyXpOwner,
613 CannotReapLockedXp,
615 XpLockExists,
617 CannotGenerateXpKey,
619 CannotTransferXp,
621 LowPulseThreshold,
623 InsufficientLiquidXp,
625 TooManyLocks,
627 TooManyReserves,
629 XpLockNotFound,
631 XpReserveNotFound,
633 InvalidMinTimeStamp,
635 LowTimeStamp,
637 XpNotReaped,
639 ReputationDeriveOverflowed,
642 XpCapOverflowed,
644 XpCapUnderflowed,
646 XpComputationError,
650 CannotLockZero,
652 CannotReserveZero,
654 XpAlreadyReaped,
656 InsufficientReserveXp,
658 XpReserveCapOverflowed,
660 XpReserveCapUnderflowed,
662 XpLockCapOverflowed,
664 XpLockCapUnderflowed,
666 }
667
668 #[pallet::event]
673 #[pallet::generate_deposit(pub(super) fn deposit_event)]
674 pub enum Event<T: Config<I>, I: 'static = ()> {
676 Xp { id: XpId<T>, xp: T::Xp },
678 XpOwner { id: XpId<T>, owner: T::AccountId },
680 XpOfOwner {
682 owner: T::AccountId,
683 ids: Vec<XpId<T>>,
684 },
685 XpEarn { id: XpId<T>, xp: T::Xp },
687 XpReap { id: XpId<T> },
689 XpSlash { id: XpId<T>, xp: T::Xp },
691 XpLock {
693 of: XpId<T>,
694 reason: T::LockReason,
695 xp: T::Xp,
696 },
697 XpLockBurn { of: XpId<T>, reason: T::LockReason },
699 XpLockSlash {
701 of: XpId<T>,
702 reason: T::LockReason,
703 xp: T::Xp,
704 },
705 XpReserve {
707 of: XpId<T>,
708 reason: T::ReserveReason,
709 xp: T::Xp,
710 },
711 XpReserveSlash {
713 of: XpId<T>,
714 reason: T::ReserveReason,
715 xp: T::Xp,
716 },
717 GenesisConfigUpdated(ForceGenesisConfig<T, I>),
719 }
720
721 #[pallet::call]
728 impl<T: Config<I>, I: 'static> Pallet<T, I> {
729 #[pallet::call_index(0)]
745 #[pallet::weight(T::WeightInfo::call())]
746 pub fn call(
747 origin: OriginFor<T>,
748 xp_id: XpId<T>,
749 call: Box<<T as Config<I>>::RuntimeCall>,
750 ) -> DispatchResult {
751 let caller = ensure_signed(origin)?;
752 Self::is_owner(&caller, &xp_id)?;
753 call.dispatch(frame_system::RawOrigin::Signed(xp_id).into())
754 .map(|_| ())
755 .map_err(|e| e.error)?;
756 Ok(())
757 }
758
759 #[pallet::call_index(1)]
772 #[pallet::weight(T::WeightInfo::handover())]
773 pub fn handover(
774 origin: OriginFor<T>,
775 xp_id: XpId<T>,
776 new_owner: T::AccountId,
777 ) -> DispatchResult {
778 let caller = ensure_signed(origin)?;
779 Self::xp_exists(&xp_id)?;
780 Self::is_owner(&caller, &xp_id)?;
781 ensure!(
782 caller != new_owner,
783 DispatchError::from(Error::<T, I>::AlreadyXpOwner)
784 );
785 Self::transfer_owner(&caller, &xp_id, &new_owner)?;
787 if !T::EmitEvents::get() {
789 Self::deposit_event(Event::XpOwner {
790 id: xp_id,
791 owner: new_owner,
792 });
793 }
794 Ok(())
795 }
796
797 #[pallet::call_index(2)]
813 #[pallet::weight(T::WeightInfo::dispose())]
814 pub fn dispose(
815 origin: OriginFor<T>,
816 owner: T::AccountId,
817 xp_id: XpId<T>,
818 ) -> DispatchResult {
819 let _caller = ensure_signed(origin)?;
820 Self::xp_exists(&xp_id)?;
821 Self::is_owner(&owner, &xp_id)?;
822 Self::try_reap(&xp_id)?;
823 if !T::EmitEvents::get() {
825 Self::deposit_event(Event::XpReap { id: xp_id.clone() });
826 }
827 Ok(())
828 }
829
830 #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
847 #[pallet::call_index(3)]
848 #[pallet::weight(T::WeightInfo::inspect_my_xp())]
849 pub fn inspect_my_xp(origin: OriginFor<T>, xp_id: XpId<T>) -> DispatchResult {
850 let caller = ensure_signed(origin)?;
851 Self::xp_exists(&xp_id)?;
852 Self::is_owner(&caller, &xp_id)?;
853 let liquid = Self::xp(&xp_id)?;
855 Self::deposit_event(Event::Xp {
857 id: xp_id.clone(),
858 xp: liquid,
859 });
860 Ok(())
861 }
862
863 #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
875 #[pallet::call_index(4)]
876 #[pallet::weight(T::WeightInfo::inspect_xp_keys_of())]
877 pub fn inspect_xp_keys_of(origin: OriginFor<T>, owner: T::AccountId) -> DispatchResult {
878 let _caller = ensure_signed(origin)?;
879 let xp_ids = Self::xp_keys(&owner)?;
880 Self::deposit_event(Event::XpOfOwner {
881 owner: owner,
882 ids: xp_ids,
883 });
884 Ok(())
885 }
886
887 #[pallet::call_index(5)]
904 #[pallet::weight(T::WeightInfo::force_handover())]
905 pub fn force_handover(
906 origin: OriginFor<T>,
907 owner: T::AccountId,
908 xp_id: XpId<T>,
909 new_owner: T::AccountId,
910 ) -> DispatchResult {
911 ensure_root(origin)?;
912 Self::xp_exists(&xp_id)?;
913 Self::is_owner(&owner, &xp_id)?;
914 ensure!(
915 owner != new_owner,
916 DispatchError::from(Error::<T, I>::AlreadyXpOwner)
917 );
918 Self::transfer_owner(&owner, &xp_id, &new_owner)?;
920 if !T::EmitEvents::get() {
922 Self::deposit_event(Event::XpOwner {
923 id: xp_id.clone(),
924 owner: new_owner.clone(),
925 });
926 }
927 Ok(())
928 }
929
930 #[pallet::call_index(6)]
952 #[pallet::weight(
953 T::WeightInfo::force_update_init_xp()
954 .max(T::WeightInfo::force_update_min_pulse())
955 .max(T::WeightInfo::force_update_pulse_factor())
956 .max(T::WeightInfo::force_update_min_time_stamp())
957 )]
958 pub fn force_genesis_config(
959 origin: OriginFor<T>,
960 field: ForceGenesisConfig<T, I>,
961 ) -> DispatchResult {
962 ensure_root(origin)?;
963 match field {
964 ForceGenesisConfig::MinPulse(min_pulse) => MinPulse::<T, I>::set(min_pulse),
965 ForceGenesisConfig::InitXp(init_xp) => InitXp::<T, I>::set(init_xp),
966 ForceGenesisConfig::PulseFactor {
967 threshold,
968 per_count,
969 } => {
970 let Some(stepper) = Stepper::<T, I>::new(threshold, per_count) else {
971 return Err(Error::<T, I>::LowPulseThreshold.into());
972 };
973 PulseFactor::<T, I>::set(stepper);
974 }
975 ForceGenesisConfig::MinTimeStamp(min_block) => {
976 let current_block = frame_system::Pallet::<T>::block_number();
977 if min_block > current_block {
978 return Err(Error::<T, I>::InvalidMinTimeStamp.into());
979 };
980 MinTimeStamp::<T, I>::set(min_block);
981 }
982 }
983 Self::deposit_event(Event::GenesisConfigUpdated(field));
984 Ok(())
985 }
986 }
987
988 impl<T: Config<I>, I: 'static> Pallet<T, I> {
1001 pub fn xp_state(key: &XpId<T>) -> Result<XpState<T, I>, DispatchError> {
1007 let xp = Self::get_xp(key)?;
1008
1009 let eligibility = Self::xp_eligibility(key)?;
1010
1011 let required_pulse = MinPulse::<T, I>::get();
1012 let multiplier = match xp.pulse.value < required_pulse {
1013 true => One::one(),
1014 false => xp.pulse.value,
1015 };
1016
1017 Ok(XpState {
1018 liquid: xp.free,
1019 reserved: xp.reserve,
1020 locked: xp.lock,
1021 multiplier,
1022 eligibility,
1023 })
1024 }
1025
1026 pub fn xp(key: &XpId<T>) -> Result<T::Xp, DispatchError> {
1030 Self::xp_exists(key)?;
1031 let liquid = Self::get_liquid_xp(key)?;
1032 Ok(liquid)
1033 }
1034
1035 pub fn xp_keys(owner: &T::AccountId) -> Result<Vec<XpId<T>>, DispatchError> {
1037 let xp_ids = Self::xp_of_owner(owner)?;
1038 Ok(xp_ids)
1039 }
1040
1041 pub fn is_disposable(key: &XpId<T>) -> DispatchResult {
1043 Self::can_reap(key)?;
1044 Ok(())
1045 }
1046
1047 pub fn xp_eligibility(key: &XpId<T>) -> Result<XpEligibility<T, I>, DispatchError> {
1064 let xp = Self::get_xp(key)?;
1065 let current_pulse = xp.pulse.value;
1066 let current_progress = xp.pulse.step;
1067
1068 let required_pulse = MinPulse::<T, I>::get();
1069 let pulse_factor = PulseFactor::<T, I>::get();
1070
1071 if current_pulse >= required_pulse {
1073 return Ok(XpEligibility::Earning);
1074 }
1075
1076 let threshold = pulse_factor.threshold;
1077 let per_action = pulse_factor.per_count;
1078
1079 ensure!(!per_action.is_zero(), Error::<T, I>::XpComputationError);
1080
1081 let zero = T::Pulse::zero();
1082 let one = T::Pulse::one();
1083
1084 let ceil_div_pulse =
1085 |value: T::Pulse, by: T::Pulse| -> Result<T::Pulse, DispatchError> {
1086 ensure!(!by.is_zero(), Error::<T, I>::XpComputationError);
1087
1088 let adjusted = value.checked_sub(&one).unwrap_or(zero);
1089
1090 adjusted
1091 .checked_div(&by)
1092 .and_then(|v| v.checked_add(&one))
1093 .ok_or(Error::<T, I>::XpComputationError.into())
1094 };
1095
1096 let remaining_pulses = required_pulse
1098 .checked_sub(¤t_pulse)
1099 .ok_or(Error::<T, I>::XpComputationError)?;
1100
1101 let actions_per_pulse = ceil_div_pulse(threshold, per_action)?;
1103
1104 let remaining_progress = threshold.checked_sub(¤t_progress).unwrap_or(zero);
1106 let actions_to_next_pulse = ceil_div_pulse(remaining_progress, per_action)?;
1107
1108 let extra_pulses = remaining_pulses.checked_sub(&one).unwrap_or(zero);
1110
1111 let extra_actions = extra_pulses
1112 .checked_mul(&actions_per_pulse)
1113 .ok_or(Error::<T, I>::XpComputationError)?;
1114
1115 let total_actions = actions_to_next_pulse
1116 .checked_add(&extra_actions)
1117 .ok_or(Error::<T, I>::XpComputationError)?;
1118
1119 Ok(XpEligibility::Progressing(total_actions))
1120 }
1121
1122 pub fn xp_multiplier(key: &XpId<T>) -> Result<Option<T::Pulse>, DispatchError> {
1138 let xp = Self::get_xp(key)?;
1139 let required_pulse = MinPulse::<T, I>::get();
1140
1141 let multiplier = match xp.pulse.value < required_pulse {
1142 true => return Ok(None),
1144 false => xp.pulse.value,
1146 };
1147
1148 let current_block = frame_system::Pallet::<T>::block_number();
1149
1150 if xp.timestamp >= current_block {
1151 return Ok(None);
1152 }
1153
1154 Ok(Some(multiplier))
1155 }
1156
1157 pub fn xp_progress(key: &XpId<T>) -> Result<XpProgress<T, I>, DispatchError> {
1164 let xp = Self::get_xp(key)?;
1165 let config = PulseFactor::<T, I>::get();
1166
1167 Ok(XpProgress {
1168 level: xp.pulse.value,
1169 progress: xp.pulse.step,
1170 threshold: config.threshold,
1171 per_action: config.per_count,
1172 })
1173 }
1174
1175 pub fn earn_preview(key: &XpId<T>, raw: T::Xp) -> Result<XpState<T, I>, DispatchError> {
1192 let xp = Self::get_xp(key)?;
1193
1194 let reward = Self::quote_earn_xp(key, raw)?;
1196
1197 let new_free = xp
1199 .free
1200 .checked_add(&reward)
1201 .ok_or(Error::<T, I>::XpCapOverflowed)?;
1202
1203 let mut next_pulse = xp.pulse.clone();
1205 let config = PulseFactor::<T, I>::get();
1206
1207 <Pallet<T, I> as DiscreteAccumulator>::increment(&mut next_pulse, &config);
1208
1209 let next_key = key; let next_eligibility = match next_pulse.value >= MinPulse::<T, I>::get() {
1212 true => XpEligibility::Earning,
1213 false => Self::xp_eligibility(next_key)?,
1214 };
1215
1216 let next_multiplier = match next_eligibility {
1217 XpEligibility::Earning => next_pulse.value,
1218 _ => T::Pulse::one(),
1219 };
1220
1221 Ok(XpState {
1222 liquid: new_free,
1223 reserved: xp.reserve,
1224 locked: xp.lock,
1225 multiplier: next_multiplier,
1226 eligibility: next_eligibility,
1227 })
1228 }
1229
1230 pub fn xp_last_earn(key: &XpId<T>) -> Result<BlockNumberFor<T>, DispatchError> {
1238 let xp = Self::get_xp(key)?;
1239 Ok(xp.timestamp)
1240 }
1241 }
1242}
1243
1244#[cfg(test)]
1250mod ext_tests {
1251
1252 use crate::{
1258 mock::*,
1259 types::{ForceGenesisConfig, IdXp, XpEligibility},
1260 };
1261
1262 use frame_suite::xp::{XpLock, XpMutate, XpOwner, XpReserve, XpSystem};
1264
1265 use frame_support::{assert_err, assert_ok, traits::VariantCountOf};
1267
1268 use sp_runtime::{BoundedVec, DispatchError};
1270
1271 #[test]
1276 fn pulse_factor_instance_check() {
1277 xp_test_ext().execute_with(|| {
1278 let threshold_1 = 100;
1279 let per_count_1 = 10;
1280
1281 let threshold_2 = 1000;
1282 let per_count_2 = 100;
1283
1284 let old_pulsefactor_instance1 = PulseFactor::get();
1285 let old_pulsefactor_instance2 = PulseFactor2::get();
1286 assert_eq!(
1287 old_pulsefactor_instance1,
1288 Stepper::new(50u8.into(), 10u8.into()).unwrap(),
1289 );
1290 assert_eq!(
1291 old_pulsefactor_instance2,
1292 Stepper2::new(20u8.into(), 6u8.into()).unwrap(),
1293 );
1294
1295 let stepper_1 = Stepper::new(threshold_1, per_count_1).unwrap();
1296 let stepper_2 = Stepper2::new(threshold_2, per_count_2).unwrap();
1297
1298 PulseFactor::set(stepper_1.clone());
1299 PulseFactor2::set(stepper_2.clone());
1300
1301 assert_eq!(PulseFactor::get(), stepper_1);
1302 assert_eq!(PulseFactor2::get(), stepper_2);
1303 });
1304 }
1305
1306 #[test]
1307 fn min_pulse_instance_check() {
1308 xp_test_ext().execute_with(|| {
1309 let min_pulse_1 = 10;
1310 let min_pulse_2 = 15;
1311
1312 let old_minpulse_instance1 = MinPulse::get();
1313 let old_min_pulse_instance2 = MinPulse2::get();
1314 assert_eq!(old_minpulse_instance1, 1);
1315 assert_eq!(old_min_pulse_instance2, 5);
1316
1317 MinPulse::set(min_pulse_1);
1318 MinPulse2::set(min_pulse_2);
1319 assert_eq!(MinPulse::get(), 10);
1320 assert_eq!(MinPulse2::get(), 15);
1321 });
1322 }
1323
1324 #[test]
1325 fn init_xp_instance_check() {
1326 xp_test_ext().execute_with(|| {
1327 let init_xp_1 = 5;
1328 let init_xp_2 = 3;
1329
1330 let old_initxp_instance1 = InitXp::get();
1331 let old_initxp_instance2 = InitXp2::get();
1332 assert_eq!(old_initxp_instance1, 10);
1333 assert_eq!(old_initxp_instance2, 1);
1334
1335 InitXp::set(init_xp_1);
1336 InitXp2::set(init_xp_2);
1337 assert_eq!(InitXp::get(), 5);
1338 assert_eq!(InitXp2::get(), 3);
1339 });
1340 }
1341
1342 #[test]
1343 fn min_time_stamp_instance_check() {
1344 xp_test_ext().execute_with(|| {
1345 let min_time_stamp_1 = 5;
1346 let min_time_stamp_2 = 10;
1347
1348 let old_mintimestamp_instance1 = MinTimeStamp::get();
1349 let old_mintimestamp_instance2 = MinTimeStamp2::get();
1350 assert_eq!(old_mintimestamp_instance1, 0);
1351 assert_eq!(old_mintimestamp_instance2, 0);
1352
1353 MinTimeStamp::set(min_time_stamp_1);
1354 MinTimeStamp2::set(min_time_stamp_2);
1355 assert_eq!(MinTimeStamp::get(), 5);
1356 assert_eq!(MinTimeStamp2::get(), 10);
1357 });
1358 }
1359
1360 #[test]
1361 fn xp_of_instance_check() {
1362 xp_test_ext().execute_with(|| {
1363 let xp_1 = MockXp::default();
1364 XpOf::insert(XP_ALPHA, xp_1);
1365
1366 let xp_2 = MockXp2::default();
1367 XpOf2::insert(XP_BETA, xp_2);
1368
1369 assert!(XpOf::contains_key(XP_ALPHA));
1370 assert!(XpOf2::contains_key(XP_BETA));
1371
1372 assert!(!XpOf::contains_key(XP_BETA));
1373 assert!(!XpOf2::contains_key(XP_ALPHA));
1374 });
1375 }
1376
1377 #[test]
1378 fn xp_owners_instance_check() {
1379 xp_test_ext().execute_with(|| {
1380 XpOwners::insert((ALICE, XP_ALPHA), ());
1381
1382 XpOwners2::insert((BOB, XP_BETA), ());
1383
1384 assert!(XpOwners::contains_key((ALICE, XP_ALPHA)));
1385 assert!(XpOwners2::contains_key((BOB, XP_BETA)));
1386 assert!(!XpOwners::contains_key((BOB, XP_BETA)));
1387 assert!(!XpOwners2::contains_key((ALICE, XP_ALPHA)));
1388 });
1389 }
1390
1391 #[test]
1392 fn reserved_xp_of_instance_check() {
1393 xp_test_ext().execute_with(|| {
1394 let reserve_1 = IdXp::new(STAKING, DEFAULT_POINTS);
1395
1396 ReservedXpOf::try_mutate(XP_ALPHA, |value| {
1397 let vec = value.get_or_insert_with(|| {
1398 BoundedVec::<IdXp<Reason, u64>, VariantCountOf<Reason>>::default()
1399 });
1400 vec.try_push(reserve_1)
1401 })
1402 .unwrap();
1403
1404 let reserve_2 = IdXp::new(GOVERNANCE, DEFAULT_POINTS);
1405
1406 ReservedXpOf2::try_mutate(XP_BETA, |value| {
1407 let vec = value.get_or_insert_with(|| {
1408 BoundedVec::<IdXp<Reason, u64>, VariantCountOf<Reason>>::default()
1409 });
1410 vec.try_push(reserve_2)
1411 })
1412 .unwrap();
1413
1414 assert!(ReservedXpOf::contains_key(XP_ALPHA));
1415 assert!(ReservedXpOf2::contains_key(XP_BETA));
1416 assert!(!ReservedXpOf::contains_key(XP_BETA));
1417 assert!(!ReservedXpOf2::contains_key(XP_ALPHA));
1418 });
1419 }
1420
1421 #[test]
1422 fn locked_xp_of_instance_check() {
1423 xp_test_ext().execute_with(|| {
1424 let lock_1 = IdXp::new(STAKING, DEFAULT_POINTS);
1425
1426 LockedXpOf::try_mutate(XP_ALPHA, |value| {
1427 let vec = value.get_or_insert_with(|| {
1428 BoundedVec::<IdXp<Reason, u64>, VariantCountOf<Reason>>::default()
1429 });
1430 vec.try_push(lock_1)
1431 })
1432 .unwrap();
1433
1434 let lock_2 = IdXp::new(GOVERNANCE, DEFAULT_POINTS);
1435 LockedXpOf2::try_mutate(XP_BETA, |value| {
1436 let vec = value.get_or_insert_with(|| {
1437 BoundedVec::<IdXp<Reason, u64>, VariantCountOf<Reason>>::default()
1438 });
1439 vec.try_push(lock_2)
1440 })
1441 .unwrap();
1442
1443 assert!(LockedXpOf::contains_key(XP_ALPHA));
1444 assert!(LockedXpOf2::contains_key(XP_BETA));
1445 assert!(!LockedXpOf::contains_key(XP_BETA));
1446 assert!(!LockedXpOf2::contains_key(XP_ALPHA));
1447 });
1448 }
1449
1450 #[test]
1451 fn reaped_xp_instance_check() {
1452 xp_test_ext().execute_with(|| {
1453 ReapedXp::insert(XP_ALPHA, ());
1454 ReapedXp2::insert(XP_BETA, ());
1455
1456 assert!(ReapedXp::contains_key(XP_ALPHA));
1457 assert!(ReapedXp2::contains_key(XP_BETA));
1458
1459 assert!(!ReapedXp::contains_key(XP_BETA));
1460 assert!(!ReapedXp2::contains_key(XP_ALPHA));
1461 });
1462 }
1463
1464 #[test]
1469 fn xp_eligibility_success_already_reputed() {
1470 xp_test_ext().execute_with(|| {
1471 Pallet::new_xp(&ALICE, &XP_ALPHA);
1472 let xp = Pallet::get_xp(&XP_ALPHA).unwrap();
1473 let min_pulse = MinPulse::get();
1474 assert!(xp.pulse.value < min_pulse);
1475
1476 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1477 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1478 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1479 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1480 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1481
1482 let xp = Pallet::get_xp(&XP_ALPHA).unwrap();
1483 let min_pulse = MinPulse::get();
1484 assert!(xp.pulse.value >= min_pulse);
1485
1486 let status = Pallet::xp_eligibility(&XP_ALPHA).unwrap();
1487 assert_eq!(status, XpEligibility::Earning);
1488 })
1489 }
1490
1491 #[test]
1492 fn xp_eligibility_success_edge_cases() {
1493 xp_test_ext().execute_with(|| {
1494 Pallet::new_xp(&ALICE, &XP_ALPHA);
1495 let stepper = Stepper::new(20, 6).unwrap();
1499 PulseFactor::put(stepper);
1500
1501 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1503 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1504
1505 let xp = Pallet::get_xp(&XP_ALPHA).unwrap();
1506 let min_pulse = MinPulse::get();
1507 assert!(xp.pulse.value < min_pulse);
1508 assert_eq!(xp.pulse.step, 12);
1509
1510 let status = Pallet::xp_eligibility(&XP_ALPHA).unwrap();
1511 assert_eq!(status, XpEligibility::Progressing(2));
1512
1513 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1514 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1515
1516 let xp = Pallet::get_xp(&XP_ALPHA).unwrap();
1517 let min_pulse = MinPulse::get();
1518 assert!(xp.pulse.value >= min_pulse);
1519 assert_eq!(xp.pulse.step, 4);
1520
1521 let status = Pallet::xp_eligibility(&XP_ALPHA).unwrap();
1522 assert_eq!(status, XpEligibility::Earning);
1523 })
1524 }
1525
1526 #[test]
1527 fn xp_eligibility_success_calls_to_reach_reputed() {
1528 xp_test_ext().execute_with(|| {
1529 Pallet::new_xp(&ALICE, &XP_ALPHA);
1530 let xp = Pallet::get_xp(&XP_ALPHA).unwrap();
1531 let min_pulse = MinPulse::get();
1532 assert!(xp.pulse.value < min_pulse);
1533
1534 let status = Pallet::xp_eligibility(&XP_ALPHA).unwrap();
1535 assert_eq!(status, XpEligibility::Progressing(5));
1536
1537 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1538
1539 let status = Pallet::xp_eligibility(&XP_ALPHA).unwrap();
1540
1541 assert_eq!(status, XpEligibility::Progressing(4));
1542
1543 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1544 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1545
1546 let status = Pallet::xp_eligibility(&XP_ALPHA).unwrap();
1547
1548 assert_eq!(status, XpEligibility::Progressing(2));
1549
1550 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1551 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1552
1553 let xp = Pallet::get_xp(&XP_ALPHA).unwrap();
1554 let min_pulse = MinPulse::get();
1555 assert!(xp.pulse.value >= min_pulse);
1556
1557 let status = Pallet::xp_eligibility(&XP_ALPHA).unwrap();
1558 assert_eq!(status, XpEligibility::Earning);
1559 })
1560 }
1561
1562 #[test]
1563 fn xp_multiplier_less_than_min_pulse() {
1564 xp_test_ext().execute_with(|| {
1565 Pallet::new_xp(&ALICE, &XP_ALPHA);
1566
1567 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1568 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1569 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1570 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1571
1572 let xp = Pallet::get_xp(&XP_ALPHA).unwrap();
1573 let min_pulse = MinPulse::get();
1574 assert!(xp.pulse.value < min_pulse);
1575
1576 let current_multiplier = Pallet::xp_multiplier(&XP_ALPHA).unwrap();
1577 assert!(current_multiplier.is_none());
1578 })
1579 }
1580
1581 #[test]
1582 fn xp_multiplier_same_block_protection() {
1583 xp_test_ext().execute_with(|| {
1584 System::set_block_number(1);
1585 Pallet::new_xp(&ALICE, &XP_ALPHA);
1586 System::set_block_number(12);
1587 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1588 System::set_block_number(13);
1589 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1590 System::set_block_number(14);
1591 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1592 System::set_block_number(15);
1593 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1594 System::set_block_number(16);
1595 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1596 System::set_block_number(17);
1597 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1598
1599 let xp = Pallet::get_xp(&XP_ALPHA).unwrap();
1600 let min_pulse = MinPulse::get();
1601 assert!(xp.pulse.value == min_pulse);
1602 let current_multiplier = Pallet::xp_multiplier(&XP_ALPHA).unwrap();
1603 assert!(current_multiplier.is_none());
1604 })
1605 }
1606
1607 #[test]
1608 fn xp_multiplier_success() {
1609 xp_test_ext().execute_with(|| {
1610 System::set_block_number(1);
1611 Pallet::new_xp(&ALICE, &XP_ALPHA);
1612 System::set_block_number(12);
1613 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1614 System::set_block_number(13);
1615 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1616 System::set_block_number(14);
1617 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1618 System::set_block_number(15);
1619 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1620 System::set_block_number(16);
1621 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1622
1623 let xp = Pallet::get_xp(&XP_ALPHA).unwrap();
1624 let min_pulse = MinPulse::get();
1625 assert!(xp.pulse.value == min_pulse);
1626 System::set_block_number(17);
1627 let current_multiplier = Pallet::xp_multiplier(&XP_ALPHA).unwrap();
1628 assert_eq!(current_multiplier, Some(1));
1629
1630 Pallet::set_lock(&XP_ALPHA, &STAKING, DEFAULT_POINTS).unwrap();
1631 System::set_block_number(20);
1632 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1633 System::set_block_number(21);
1634 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1635 System::set_block_number(22);
1636 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1637 System::set_block_number(23);
1638 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1639 System::set_block_number(24);
1640 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1641
1642 let xp = Pallet::get_xp(&XP_ALPHA).unwrap();
1643 let min_pulse = MinPulse::get();
1644 assert!(xp.pulse.value > min_pulse);
1645 dbg!(xp.pulse.value);
1646 System::set_block_number(25);
1647 let current_multiplier = Pallet::xp_multiplier(&XP_ALPHA).unwrap();
1648 assert_eq!(current_multiplier, Some(2));
1649 })
1650 }
1651
1652 #[test]
1653 fn xp_state_success() {
1654 xp_test_ext().execute_with(|| {
1655 System::set_block_number(5);
1656 Pallet::new_xp(&ALICE, &XP_ALPHA);
1657
1658 System::set_block_number(20);
1659 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1660 System::set_block_number(21);
1661 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1662 System::set_block_number(22);
1663 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1664 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1665
1666 let xp_state = Pallet::xp_state(&XP_ALPHA).unwrap();
1667 assert_eq!(xp_state.liquid, 10);
1668 assert_eq!(xp_state.reserved, 0);
1669 assert_eq!(xp_state.locked, 0);
1670 assert_eq!(xp_state.multiplier, 1);
1671 assert_eq!(xp_state.eligibility, XpEligibility::Progressing(1));
1672
1673 System::set_block_number(23);
1674 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1675 Pallet::set_lock(&XP_ALPHA, &STAKING, DEFAULT_POINTS).unwrap();
1676 Pallet::set_reserve(&XP_ALPHA, &STAKING, 25).unwrap();
1677
1678 System::set_block_number(24);
1679 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1680
1681 let xp_state = Pallet::xp_state(&XP_ALPHA).unwrap();
1682 assert_eq!(xp_state.liquid, 20);
1683 assert_eq!(xp_state.reserved, 25);
1684 assert_eq!(xp_state.locked, DEFAULT_POINTS);
1685 assert_eq!(xp_state.multiplier, 1);
1686 assert_eq!(xp_state.eligibility, XpEligibility::Earning);
1687 })
1688 }
1689
1690 #[test]
1691 fn fetch_pulse_progress() {
1692 xp_test_ext().execute_with(|| {
1693 System::set_block_number(5);
1694 Pallet::new_xp(&ALICE, &XP_ALPHA);
1695
1696 System::set_block_number(20);
1697 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1698 System::set_block_number(21);
1699 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1700
1701 let pulse_progress = Pallet::xp_progress(&XP_ALPHA).unwrap();
1702 assert_eq!(pulse_progress.progress, 20);
1703 assert_eq!(pulse_progress.level, 0);
1704 assert_eq!(pulse_progress.threshold, 50);
1705 assert_eq!(pulse_progress.per_action, 10);
1706
1707 System::set_block_number(22);
1708 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1709 System::set_block_number(23);
1710 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1711 System::set_block_number(24);
1712 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1713
1714 let pulse_progress = Pallet::xp_progress(&XP_ALPHA).unwrap();
1715 assert_eq!(pulse_progress.progress, 0);
1716 assert_eq!(pulse_progress.level, 1);
1717 assert_eq!(pulse_progress.threshold, 50);
1718 assert_eq!(pulse_progress.per_action, 10);
1719 })
1720 }
1721
1722 #[test]
1723 fn earn_preview_below_min_pulse_returns_zero_reward_and_required_steps() {
1724 xp_test_ext().execute_with(|| {
1725 System::set_block_number(10);
1726 Pallet::new_xp(&ALICE, &XP_ALPHA);
1727
1728 let earn_preview = Pallet::earn_preview(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1729 assert_eq!(earn_preview.liquid, DEFAULT_POINTS);
1730 assert_eq!(earn_preview.reserved, 0);
1731 assert_eq!(earn_preview.locked, 0);
1732 assert_eq!(earn_preview.multiplier, 1);
1733 assert_eq!(earn_preview.eligibility, XpEligibility::Progressing(5));
1734 })
1735 }
1736
1737 #[test]
1738 fn earn_preview_will_repute_progress() {
1739 xp_test_ext().execute_with(|| {
1740 System::set_block_number(10);
1741 Pallet::new_xp(&ALICE, &XP_ALPHA);
1742
1743 System::set_block_number(22);
1744 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1745 System::set_block_number(23);
1746 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1747 System::set_block_number(24);
1748 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1749
1750 System::set_block_number(25);
1751 let earn_preview = Pallet::earn_preview(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1752 assert_eq!(earn_preview.liquid, DEFAULT_POINTS);
1753 assert_eq!(earn_preview.reserved, 0);
1754 assert_eq!(earn_preview.locked, 0);
1755 assert_eq!(earn_preview.multiplier, 1);
1756 assert_eq!(earn_preview.eligibility, XpEligibility::Progressing(2));
1757
1758 System::set_block_number(25);
1759 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1760
1761 System::set_block_number(26);
1762 let earn_preview = Pallet::earn_preview(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1763 assert_eq!(earn_preview.liquid, DEFAULT_POINTS);
1764 assert_eq!(earn_preview.reserved, 0);
1765 assert_eq!(earn_preview.locked, 0);
1766 assert_eq!(earn_preview.multiplier, 1);
1767 assert_eq!(earn_preview.eligibility, XpEligibility::Earning);
1768 })
1769 }
1770
1771 #[test]
1772 fn earn_preview_above_min_pulse() {
1773 xp_test_ext().execute_with(|| {
1774 System::set_block_number(10);
1775 Pallet::new_xp(&ALICE, &XP_ALPHA);
1776
1777 System::set_block_number(22);
1778 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1779 System::set_block_number(23);
1780 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1781 System::set_block_number(24);
1782 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1783 System::set_block_number(25);
1784 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1785 System::set_block_number(26);
1786 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1787
1788 System::set_block_number(27);
1789 let earn_preview = Pallet::earn_preview(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1790 assert_eq!(earn_preview.liquid, 20);
1791 assert_eq!(earn_preview.reserved, 0);
1792 assert_eq!(earn_preview.locked, 0);
1793 assert_eq!(earn_preview.multiplier, 1);
1794 assert_eq!(earn_preview.eligibility, XpEligibility::Earning);
1795 })
1796 }
1797
1798 #[test]
1799 fn earn_preview_multiplier_progress_without_lock() {
1800 xp_test_ext().execute_with(|| {
1801 System::set_block_number(10);
1802 Pallet::new_xp(&ALICE, &XP_ALPHA);
1803
1804 System::set_block_number(22);
1806 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1807 System::set_block_number(23);
1808 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1809 System::set_block_number(24);
1810 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1811 System::set_block_number(25);
1812 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1813 System::set_block_number(26);
1814 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1815
1816 System::set_block_number(27);
1817 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1818 let earn_preview = Pallet::earn_preview(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1820 assert_eq!(earn_preview.liquid, 30);
1821 assert_eq!(earn_preview.reserved, 0);
1822 assert_eq!(earn_preview.locked, 0);
1823 assert_eq!(earn_preview.multiplier, 1);
1824 assert_eq!(earn_preview.eligibility, XpEligibility::Earning);
1825
1826 for n in 28..48 {
1827 System::set_block_number(n);
1828 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1829 }
1830 let earn_preview = Pallet::earn_preview(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1832 assert_eq!(earn_preview.liquid, 230);
1833 assert_eq!(earn_preview.reserved, 0);
1834 assert_eq!(earn_preview.locked, 0);
1835 assert_eq!(earn_preview.multiplier, 1);
1836 assert_eq!(earn_preview.eligibility, XpEligibility::Earning);
1837 })
1838 }
1839
1840 #[test]
1841 fn earn_preview_with_lock() {
1842 xp_test_ext().execute_with(|| {
1843 System::set_block_number(10);
1844 Pallet::new_xp(&ALICE, &XP_ALPHA);
1845
1846 System::set_block_number(22);
1847 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1848 System::set_block_number(23);
1849 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1850 System::set_block_number(24);
1851 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1852 System::set_block_number(25);
1853 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1854 System::set_block_number(26);
1855 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1856
1857 Pallet::set_lock(&XP_ALPHA, &STAKING, DEFAULT_POINTS).unwrap();
1858
1859 System::set_block_number(27);
1860 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1861 System::set_block_number(28);
1862 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1863
1864 System::set_block_number(29);
1865 let earn_preview = Pallet::earn_preview(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1866 assert_eq!(earn_preview.liquid, 40);
1867 assert_eq!(earn_preview.reserved, 0);
1868 assert_eq!(earn_preview.locked, 10);
1869 assert_eq!(earn_preview.multiplier, 1);
1870 assert_eq!(earn_preview.eligibility, XpEligibility::Earning);
1871
1872 System::set_block_number(30);
1873 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1874 System::set_block_number(31);
1875 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1876
1877 System::set_block_number(32);
1878 let earn_preview = Pallet::earn_preview(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1879 assert_eq!(earn_preview.liquid, 60);
1880 assert_eq!(earn_preview.reserved, 0);
1881 assert_eq!(earn_preview.locked, 10);
1882 assert_eq!(earn_preview.multiplier, 2);
1883 assert_eq!(earn_preview.eligibility, XpEligibility::Earning);
1884 })
1885 }
1886
1887 #[test]
1888 fn earn_preview_with_lock_multiplier_progress() {
1889 xp_test_ext().execute_with(|| {
1890 System::set_block_number(10);
1891 Pallet::new_xp(&ALICE, &XP_ALPHA);
1892
1893 System::set_block_number(22);
1894 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1895 System::set_block_number(23);
1896 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1897 System::set_block_number(24);
1898 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1899 System::set_block_number(25);
1900 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1901 System::set_block_number(26);
1902 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1903
1904 Pallet::set_lock(&XP_ALPHA, &STAKING, DEFAULT_POINTS).unwrap();
1905
1906 System::set_block_number(27);
1907 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1908 System::set_block_number(28);
1909 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1910 System::set_block_number(29);
1911 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1912 System::set_block_number(30);
1913 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1914
1915 System::set_block_number(31);
1916 let earn_preview = Pallet::earn_preview(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1917 assert_eq!(earn_preview.liquid, 60);
1918 assert_eq!(earn_preview.reserved, 0);
1919 assert_eq!(earn_preview.locked, 10);
1920 assert_eq!(earn_preview.multiplier, 2);
1921 assert_eq!(earn_preview.eligibility, XpEligibility::Earning);
1922
1923 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1924
1925 for n in 32..42 {
1926 System::set_block_number(n);
1927 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1928 }
1929
1930 let earn_preview = Pallet::earn_preview(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1932 assert_eq!(earn_preview.liquid, 320);
1933 assert_eq!(earn_preview.reserved, 0);
1934 assert_eq!(earn_preview.locked, 10);
1935 assert_eq!(earn_preview.multiplier, 4);
1936 assert_eq!(earn_preview.eligibility, XpEligibility::Earning);
1937 })
1938 }
1939
1940 #[test]
1941 fn earn_preview_with_same_block_protection() {
1942 xp_test_ext().execute_with(|| {
1943 System::set_block_number(10);
1944 Pallet::new_xp(&ALICE, &XP_ALPHA);
1945
1946 System::set_block_number(22);
1947 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1948 System::set_block_number(23);
1949 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1950 System::set_block_number(24);
1951 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1952 System::set_block_number(25);
1953 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1954 System::set_block_number(26);
1955 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1956
1957 Pallet::set_lock(&XP_ALPHA, &STAKING, DEFAULT_POINTS).unwrap();
1958
1959 System::set_block_number(27);
1960 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1961 System::set_block_number(28);
1962 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1963 System::set_block_number(29);
1964 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1965 System::set_block_number(30);
1966 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1967 System::set_block_number(31);
1968 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1969
1970 let earn_preview = Pallet::earn_preview(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1971 assert_eq!(earn_preview.liquid, 70);
1972 assert_eq!(earn_preview.reserved, 0);
1973 assert_eq!(earn_preview.locked, 10);
1974 assert_eq!(earn_preview.multiplier, 2);
1975 assert_eq!(earn_preview.eligibility, XpEligibility::Earning);
1976 })
1977 }
1978
1979 #[test]
1980 fn earn_preview_matches_earn_xp_actual_reward() {
1981 xp_test_ext().execute_with(|| {
1982 System::set_block_number(10);
1983 Pallet::new_xp(&ALICE, &XP_ALPHA);
1984
1985 Pallet::set_lock(&XP_ALPHA, &STAKING, DEFAULT_POINTS).unwrap();
1986
1987 for n in 20..40 {
1989 System::set_block_number(n);
1990 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1991 }
1992
1993 System::set_block_number(41);
1994
1995 let earn_preview = Pallet::earn_preview(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1996 assert_eq!(earn_preview.liquid, 350);
1997 assert_eq!(earn_preview.reserved, 0);
1998 assert_eq!(earn_preview.locked, 10);
1999 assert_eq!(earn_preview.multiplier, 4);
2000 assert_eq!(earn_preview.eligibility, XpEligibility::Earning);
2001
2002 let xp = Pallet::get_xp(&XP_ALPHA).unwrap();
2003 let free_before = xp.free;
2004
2005 let actual_earn = Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
2006 let xp = Pallet::get_xp(&XP_ALPHA).unwrap();
2007 let free_after = xp.free;
2008
2009 let diff = free_after - free_before;
2010 assert_eq!(free_after, earn_preview.liquid);
2011 assert_eq!(diff, actual_earn);
2012 })
2013 }
2014
2015 #[test]
2016 fn earn_preview_err_xp_not_found() {
2017 xp_test_ext().execute_with(|| {
2018 Pallet::new_xp(&ALICE, &XP_ALPHA);
2019 assert_err!(
2020 Pallet::earn_preview(&XP_BETA, DEFAULT_POINTS),
2021 Error::XpNotFound
2022 );
2023 })
2024 }
2025
2026 #[cfg(feature = "dev")]
2031 #[test]
2032 fn inspect_my_xp_success() {
2033 xp_test_ext().execute_with(|| {
2034 Pallet::new_xp(&ALICE, &XP_ALPHA);
2035 System::set_block_number(1);
2036 assert_ok!(Xp::inspect_my_xp(RuntimeOrigin::signed(ALICE), XP_ALPHA));
2037 System::assert_last_event(
2038 Event::Xp {
2039 id: XP_ALPHA,
2040 xp: InitXp::get(),
2041 }
2042 .into(),
2043 );
2044 });
2045 }
2046
2047 #[cfg(feature = "dev")]
2048 #[test]
2049 fn inspect_my_xp_fail_xp_not_found() {
2050 xp_test_ext().execute_with(|| {
2051 Pallet::new_xp(&ALICE, &XP_ALPHA);
2052 assert_err!(
2053 Xp::inspect_my_xp(RuntimeOrigin::signed(ALICE), XP_BETA),
2054 Error::XpNotFound
2055 );
2056 });
2057 }
2058
2059 #[cfg(feature = "dev")]
2060 #[test]
2061 fn inspect_my_xp_fail_not_signed() {
2062 xp_test_ext().execute_with(|| {
2063 assert_err!(
2064 Xp::inspect_my_xp(RuntimeOrigin::root(), XP_ALPHA),
2065 DispatchError::BadOrigin
2066 );
2067 });
2068 }
2069
2070 #[cfg(feature = "dev")]
2071 #[test]
2072 fn inspect_my_xp_fail_invalid_owner() {
2073 xp_test_ext().execute_with(|| {
2074 Pallet::new_xp(&ALICE, &XP_ALPHA);
2075 assert_err!(
2076 Xp::inspect_my_xp(RuntimeOrigin::signed(BOB), XP_ALPHA),
2077 Error::InvalidXpOwner
2078 );
2079 });
2080 }
2081
2082 #[test]
2083 fn handover_success() {
2084 xp_test_ext().execute_with(|| {
2085 Pallet::new_xp(&ALICE, &XP_ALPHA);
2086 System::set_block_number(1);
2087 assert_ok!(Xp::handover(RuntimeOrigin::signed(ALICE), XP_ALPHA, BOB));
2088 assert_ok!(Pallet::is_owner(&BOB, &XP_ALPHA));
2089 System::assert_last_event(
2090 Event::XpOwner {
2091 id: XP_ALPHA,
2092 owner: BOB,
2093 }
2094 .into(),
2095 );
2096 });
2097 }
2098
2099 #[test]
2100 fn handover_fail_xp_not_found() {
2101 xp_test_ext().execute_with(|| {
2102 assert_err!(
2103 Xp::handover(RuntimeOrigin::signed(ALICE), XP_ALPHA, BOB),
2104 Error::XpNotFound
2105 );
2106 });
2107 }
2108
2109 #[test]
2110 fn handover_fail_not_signed() {
2111 xp_test_ext().execute_with(|| {
2112 assert_err!(
2113 Xp::handover(RuntimeOrigin::root(), XP_ALPHA, BOB),
2114 DispatchError::BadOrigin
2115 );
2116 });
2117 }
2118
2119 #[test]
2120 fn handover_fail_invalid_owner() {
2121 xp_test_ext().execute_with(|| {
2122 Pallet::new_xp(&ALICE, &XP_ALPHA);
2123 assert_err!(
2124 Xp::handover(RuntimeOrigin::signed(CHARLIE), XP_ALPHA, BOB),
2125 Error::InvalidXpOwner
2126 );
2127 });
2128 }
2129
2130 #[test]
2131 fn handover_fail_already_owner() {
2132 xp_test_ext().execute_with(|| {
2133 Pallet::new_xp(&ALICE, &XP_ALPHA);
2134 assert_err!(
2135 Xp::handover(RuntimeOrigin::signed(ALICE), XP_ALPHA, ALICE),
2136 Error::AlreadyXpOwner
2137 );
2138 });
2139 }
2140
2141 #[test]
2142 fn dispose_success() {
2143 xp_test_ext().execute_with(|| {
2144 MinTimeStamp::set(3);
2145 System::set_block_number(1);
2146 Pallet::new_xp(&ALICE, &XP_ALPHA);
2147 Pallet::set_xp(&XP_ALPHA, 0).unwrap();
2148 assert_ok!(Pallet::xp_exists(&XP_ALPHA));
2149 System::set_block_number(2);
2150 assert_ok!(Xp::dispose(RuntimeOrigin::signed(CHARLIE), ALICE, XP_ALPHA));
2151 assert_err!(Pallet::xp_exists(&XP_ALPHA), Error::XpNotFound);
2152 });
2153 }
2154
2155 #[test]
2156 fn dispose_fail_xp_not_found() {
2157 xp_test_ext().execute_with(|| {
2158 Pallet::new_xp(&ALICE, &XP_ALPHA);
2159
2160 assert_err!(
2161 Xp::dispose(RuntimeOrigin::signed(CHARLIE), ALICE, XP_BETA),
2162 Error::XpNotFound
2163 );
2164 });
2165 }
2166
2167 #[test]
2168 fn dispose_fail_not_owner() {
2169 xp_test_ext().execute_with(|| {
2170 Pallet::new_xp(&ALICE, &XP_ALPHA);
2171 assert_err!(
2172 Xp::dispose(RuntimeOrigin::signed(CHARLIE), BOB, XP_ALPHA),
2173 Error::InvalidXpOwner
2174 );
2175 });
2176 }
2177
2178 #[test]
2179 fn dispose_fail_xp_not_dead() {
2180 xp_test_ext().execute_with(|| {
2181 System::set_block_number(1);
2182 System::set_block_number(2);
2183 System::set_block_number(3);
2184 Pallet::new_xp(&ALICE, &XP_ALPHA);
2185 Pallet::set_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
2186 assert_err!(
2187 Xp::dispose(RuntimeOrigin::signed(CHARLIE), ALICE, XP_ALPHA),
2188 Error::XpNotDead
2189 );
2190 });
2191 }
2192
2193 #[test]
2194 fn dispose_fail_locked_xp() {
2195 xp_test_ext().execute_with(|| {
2196 MinTimeStamp::set(3);
2197 Pallet::new_xp(&ALICE, &XP_ALPHA);
2198 Pallet::set_lock(&XP_ALPHA, &STAKING, DEFAULT_POINTS).unwrap();
2199 System::set_block_number(2);
2200 assert_err!(
2201 Xp::dispose(RuntimeOrigin::signed(CHARLIE), ALICE, XP_ALPHA),
2202 Error::CannotReapLockedXp
2203 );
2204 });
2205 }
2206
2207 #[test]
2208 fn force_handover_success() {
2209 xp_test_ext().execute_with(|| {
2210 Pallet::new_xp(&ALICE, &XP_ALPHA);
2211 System::set_block_number(1);
2212 assert_ok!(Xp::force_handover(
2213 RuntimeOrigin::root(),
2214 ALICE,
2215 XP_ALPHA,
2216 BOB
2217 ));
2218 assert_ok!(Pallet::is_owner(&BOB, &XP_ALPHA));
2219 System::assert_last_event(
2220 Event::XpOwner {
2221 id: XP_ALPHA,
2222 owner: BOB,
2223 }
2224 .into(),
2225 );
2226 });
2227 }
2228
2229 #[test]
2230 fn force_handover_fail_xp_not_found() {
2231 xp_test_ext().execute_with(|| {
2232 Pallet::new_xp(&ALICE, &XP_ALPHA);
2233 assert_err!(
2234 Xp::force_handover(RuntimeOrigin::root(), ALICE, XP_BETA, BOB),
2235 Error::XpNotFound
2236 );
2237 });
2238 }
2239
2240 #[test]
2241 fn force_handover_fail_not_root() {
2242 xp_test_ext().execute_with(|| {
2243 Pallet::new_xp(&ALICE, &XP_ALPHA);
2244 assert_err!(
2245 Xp::force_handover(RuntimeOrigin::signed(CHARLIE), ALICE, XP_ALPHA, BOB),
2246 DispatchError::BadOrigin
2247 );
2248 });
2249 }
2250
2251 #[test]
2252 fn force_handover_fail_invalid_owner() {
2253 xp_test_ext().execute_with(|| {
2254 Pallet::new_xp(&ALICE, &XP_ALPHA);
2255 assert_err!(
2256 Xp::force_handover(RuntimeOrigin::root(), CHARLIE, XP_ALPHA, BOB),
2257 Error::InvalidXpOwner
2258 );
2259 });
2260 }
2261
2262 #[test]
2263 fn force_handover_fail_already_owner() {
2264 xp_test_ext().execute_with(|| {
2265 Pallet::new_xp(&ALICE, &XP_ALPHA);
2266 assert_err!(
2267 Xp::force_handover(RuntimeOrigin::root(), ALICE, XP_ALPHA, ALICE),
2268 Error::AlreadyXpOwner
2269 );
2270 });
2271 }
2272
2273 #[cfg(feature = "dev")]
2274 #[test]
2275 fn inspect_xp_keys_of_success() {
2276 xp_test_ext().execute_with(|| {
2277 Pallet::new_xp(&ALICE, &XP_ALPHA);
2278 Pallet::new_xp(&ALICE, &XP_BETA);
2279 System::set_block_number(1);
2280 assert_ok!(Xp::inspect_xp_keys_of(RuntimeOrigin::signed(ALICE), ALICE));
2281 System::assert_last_event(
2282 Event::XpOfOwner {
2283 owner: ALICE,
2284 ids: vec![XP_ALPHA, XP_BETA],
2285 }
2286 .into(),
2287 );
2288 });
2289 }
2290
2291 #[cfg(feature = "dev")]
2292 #[test]
2293 fn inspect_xp_keys_of_fail_not_signed() {
2294 xp_test_ext().execute_with(|| {
2295 Pallet::new_xp(&ALICE, &XP_ALPHA);
2296 Pallet::new_xp(&ALICE, &XP_BETA);
2297 assert_err!(
2298 Xp::inspect_xp_keys_of(RuntimeOrigin::root(), ALICE),
2299 DispatchError::BadOrigin
2300 );
2301 });
2302 }
2303
2304 #[test]
2305 fn force_genesis_config_min_pulse_success() {
2306 xp_test_ext().execute_with(|| {
2307 System::set_block_number(1);
2308 let new_min_pulse: u32 = 5;
2309 assert_ok!(Xp::force_genesis_config(
2310 RuntimeOrigin::root(),
2311 ForceGenesisConfig::MinPulse(new_min_pulse)
2312 ));
2313 assert_eq!(MinPulse::get(), new_min_pulse);
2314
2315 System::assert_last_event(
2316 Event::GenesisConfigUpdated(ForceGenesisConfig::MinPulse(new_min_pulse)).into(),
2317 );
2318 });
2319 }
2320
2321 #[test]
2322 fn force_genesis_config_min_pulse_fail_not_root() {
2323 xp_test_ext().execute_with(|| {
2324 let min_pulse = 5;
2325 assert_err!(
2326 Xp::force_genesis_config(
2327 RuntimeOrigin::signed(CHARLIE),
2328 ForceGenesisConfig::MinPulse(min_pulse)
2329 ),
2330 DispatchError::BadOrigin
2331 );
2332 assert_eq!(MinPulse::get(), 1);
2333 });
2334 }
2335
2336 #[test]
2337 fn force_genesis_config_init_xp_success() {
2338 xp_test_ext().execute_with(|| {
2339 System::set_block_number(1);
2340 let new_init_xp = 50;
2341 assert_ok!(Xp::force_genesis_config(
2342 RuntimeOrigin::root(),
2343 ForceGenesisConfig::InitXp(new_init_xp)
2344 ));
2345 assert_eq!(InitXp::get(), new_init_xp);
2346 System::assert_last_event(
2347 Event::GenesisConfigUpdated(ForceGenesisConfig::InitXp(new_init_xp)).into(),
2348 );
2349 });
2350 }
2351
2352 #[test]
2353 fn force_genesis_config_init_xp_fail_not_root() {
2354 xp_test_ext().execute_with(|| {
2355 let new_init_xp = 50;
2356 assert_err!(
2357 Xp::force_genesis_config(
2358 RuntimeOrigin::signed(CHARLIE),
2359 ForceGenesisConfig::InitXp(new_init_xp)
2360 ),
2361 DispatchError::BadOrigin
2362 );
2363 assert_eq!(InitXp::get(), 10);
2364 });
2365 }
2366
2367 #[test]
2368 fn force_genesis_config_min_time_stamp_success() {
2369 xp_test_ext().execute_with(|| {
2370 System::set_block_number(1);
2371 let new_min_time_stamp = 4;
2372 System::set_block_number(5);
2373 assert_ok!(Xp::force_genesis_config(
2374 RuntimeOrigin::root(),
2375 ForceGenesisConfig::MinTimeStamp(new_min_time_stamp)
2376 ));
2377 assert_eq!(MinTimeStamp::get(), new_min_time_stamp);
2378 System::assert_last_event(
2379 Event::GenesisConfigUpdated(ForceGenesisConfig::MinTimeStamp(new_min_time_stamp))
2380 .into(),
2381 );
2382 });
2383 }
2384
2385 #[test]
2386 fn force_genesis_config_min_time_stamp_fail_not_root() {
2387 xp_test_ext().execute_with(|| {
2388 let new_min_time_stamp = 4;
2389 assert_err!(
2390 Xp::force_genesis_config(
2391 RuntimeOrigin::signed(ALICE),
2392 ForceGenesisConfig::MinTimeStamp(new_min_time_stamp)
2393 ),
2394 DispatchError::BadOrigin
2395 );
2396 });
2397 }
2398
2399 #[test]
2400 fn force_genesis_config_min_time_stamp_fail_invalid_min_time_stamp() {
2401 xp_test_ext().execute_with(|| {
2402 let new_min_time_stamp = 4;
2403 System::set_block_number(3);
2405 assert_err!(
2406 Xp::force_genesis_config(
2407 RuntimeOrigin::root(),
2408 ForceGenesisConfig::MinTimeStamp(new_min_time_stamp)
2409 ),
2410 Error::InvalidMinTimeStamp
2411 );
2412 });
2413 }
2414
2415 #[test]
2416 fn force_genesis_config_pulse_factor_success() {
2417 xp_test_ext().execute_with(|| {
2418 System::set_block_number(1);
2419 let threshold = 100;
2420 let per_count = 10;
2421 assert_ok!(Xp::force_genesis_config(
2422 RuntimeOrigin::root(),
2423 ForceGenesisConfig::PulseFactor {
2424 threshold,
2425 per_count
2426 }
2427 ));
2428 let stepper = PulseFactor::get();
2429 assert_eq!(stepper.threshold, threshold);
2430 assert_eq!(stepper.per_count, per_count);
2431 System::assert_last_event(
2432 Event::GenesisConfigUpdated(ForceGenesisConfig::PulseFactor {
2433 threshold,
2434 per_count,
2435 })
2436 .into(),
2437 );
2438 })
2439 }
2440
2441 #[test]
2442 fn force_genesis_config_pulse_factor_fail_low_pulse_threshold() {
2443 xp_test_ext().execute_with(|| {
2444 let threshold = 100;
2445 let per_count = 110;
2446 assert_err!(
2447 Xp::force_genesis_config(
2448 RuntimeOrigin::root(),
2449 ForceGenesisConfig::PulseFactor {
2450 threshold,
2451 per_count
2452 }
2453 ),
2454 Error::LowPulseThreshold
2455 );
2456 });
2457 }
2458
2459 #[test]
2460 fn force_genesis_config_pulse_factor_fail_not_root() {
2461 xp_test_ext().execute_with(|| {
2462 let threshold = 100;
2463 let per_count = 10;
2464 assert_err!(
2465 Xp::force_genesis_config(
2466 RuntimeOrigin::signed(ALICE),
2467 ForceGenesisConfig::PulseFactor {
2468 threshold,
2469 per_count
2470 }
2471 ),
2472 DispatchError::BadOrigin
2473 );
2474 });
2475 }
2476
2477 #[test]
2478 fn call_success() {
2479 xp_test_ext().execute_with(|| {
2480 Pallet::new_xp(&ALICE, &XP_ALPHA);
2481 Pallet::new_xp(&BOB, &XP_BETA);
2482
2483 let call = Box::new(Call::Xp(crate::Call::handover {
2484 xp_id: XP_ALPHA,
2485 new_owner: BOB,
2486 }));
2487 assert_ok!(Pallet::is_owner(&ALICE, &XP_ALPHA));
2488 System::set_block_number(2);
2489 assert_ok!(Xp::call(RuntimeOrigin::signed(ALICE), XP_ALPHA, call));
2490 assert_err!(Pallet::is_owner(&ALICE, &XP_ALPHA), Error::InvalidXpOwner);
2491 assert_ok!(Pallet::is_owner(&BOB, &XP_ALPHA));
2492 System::assert_last_event(
2493 Event::XpOwner {
2494 id: XP_ALPHA,
2495 owner: BOB,
2496 }
2497 .into(),
2498 );
2499 });
2500 }
2501
2502 #[test]
2503 fn call_fail_invalid_owner() {
2504 xp_test_ext().execute_with(|| {
2505 Pallet::new_xp(&ALICE, &XP_ALPHA);
2506 Pallet::new_xp(&BOB, &XP_BETA);
2507
2508 let call = Box::new(Call::Xp(crate::Call::handover {
2509 xp_id: XP_ALPHA,
2510 new_owner: BOB,
2511 }));
2512 assert_ok!(Pallet::is_owner(&ALICE, &XP_ALPHA));
2513 assert_err!(
2514 Xp::call(RuntimeOrigin::signed(ALICE), XP_BETA, call),
2515 Error::InvalidXpOwner
2516 );
2517 });
2518 }
2519
2520 #[test]
2521 fn call_fail_bad_origin() {
2522 xp_test_ext().execute_with(|| {
2523 Pallet::new_xp(&ALICE, &XP_ALPHA);
2524 Pallet::new_xp(&BOB, &XP_BETA);
2525
2526 let call = Box::new(Call::Xp(crate::Call::handover {
2527 xp_id: XP_ALPHA,
2528 new_owner: BOB,
2529 }));
2530 assert_ok!(Pallet::is_owner(&ALICE, &XP_ALPHA));
2531 assert_err!(
2532 Xp::call(RuntimeOrigin::root(), XP_ALPHA, call),
2533 DispatchError::BadOrigin
2534 );
2535 });
2536 }
2537}