1#![cfg_attr(not(feature = "std"), no_std)]
241
242#[cfg(feature = "runtime-benchmarks")]
247mod benchmarking;
248#[cfg(test)]
249mod mock;
250#[cfg(test)]
251mod tests;
252mod xp;
253mod fungible;
254pub mod types;
255pub mod weights;
256
257pub use pallet::*;
262
263#[frame_support::pallet]
264pub mod pallet {
265
266 use core::fmt::Debug;
272
273 use crate::{
275 types::{
276 ForceGenesisConfig, GenesisAcc, IdXp, Stepper, Xp, XpEligibility, XpId, XpProgress,
277 XpState,
278 },
279 weights::WeightInfo,
280 };
281
282 use frame_suite::{
284 accumulators::DiscreteAccumulator,
285 base::{Asset, Delimited, RuntimeEnum, Time},
286 xp::{
287 XpLockListener, XpMutate, XpMutateListener, XpOwner, XpOwnerListener, XpReap,
288 XpReapListener, XpReserveListener, XpSystem, XpSystemExtensions,
289 },
290 };
291
292 use frame_support::{
294 dispatch::{DispatchResult, GetDispatchInfo},
295 pallet_prelude::*,
296 traits::{IsSubType, VariantCount, VariantCountOf},
297 Blake2_128Concat,
298 };
299
300 use frame_system::{
302 ensure_root,
303 pallet_prelude::{BlockNumberFor, *},
304 };
305
306 use scale_info::prelude::boxed::Box;
308
309 use sp_runtime::{traits::Dispatchable, DispatchError, Vec};
311
312 #[pallet::pallet]
352 pub struct Pallet<T, I = ()>(PhantomData<(T, I)>);
353
354 #[pallet::config]
371 pub trait Config<I: 'static = ()>: frame_system::Config {
372 type RuntimeEvent: From<Event<Self, I>>
376 + IsType<<Self as frame_system::Config>::RuntimeEvent>;
377
378 type RuntimeCall: Parameter
380 + Dispatchable<RuntimeOrigin = Self::RuntimeOrigin>
381 + GetDispatchInfo
382 + From<frame_system::Call<Self>>
383 + IsSubType<Call<Self, I>>
384 + IsType<<Self as frame_system::Config>::RuntimeCall>;
385
386 type ReserveReason: RuntimeEnum + Delimited + Copy + VariantCount;
392
393 type LockReason: RuntimeEnum + Delimited + Copy + VariantCount;
399
400 type Xp: Asset + From<Self::Pulse>;
404
405 type Pulse: Time;
408
409 type WeightInfo: WeightInfo;
413
414 type Extensions: XpSystemExtensions<Via = Pallet<Self, I>>
425 + XpOwnerListener
426 + XpMutateListener
427 + XpReserveListener
428 + XpLockListener
429 + XpReapListener;
430
431 #[pallet::constant]
440 type EmitEvents: Get<bool> + Clone + Debug;
441 }
442
443 #[pallet::genesis_config]
452 pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
453 pub min_pulse: T::Pulse,
458
459 pub init_xp: T::Xp,
464
465 pub pulse_factor: Stepper<T, I>,
470
471 pub genesis_acc: Vec<GenesisAcc<T::AccountId, XpId<T>>>,
476 }
477
478 impl<T: Config<I>, I: 'static> Default for GenesisConfig<T, I> {
480 fn default() -> Self {
481 Self {
482 min_pulse: 3u32.into(),
483 init_xp: 1u32.into(),
484 pulse_factor: Stepper::<T, I>::new(50u8.into(), 10u8.into()).unwrap(),
485 genesis_acc: Vec::new(),
486 }
487 }
488 }
489
490 #[pallet::genesis_build]
492 impl<T: Config<I>, I: 'static> BuildGenesisConfig for GenesisConfig<T, I> {
493 fn build(&self) {
494 MinPulse::<T, I>::put(self.min_pulse);
495 InitXp::<T, I>::put(self.init_xp);
496 MinTimeStamp::<T, I>::put(BlockNumberFor::<T>::zero());
497 PulseFactor::<T, I>::put(&self.pulse_factor);
498
499 for acc_struct in &self.genesis_acc {
500 Pallet::<T, I>::new_xp(&acc_struct.owner, &acc_struct.id)
501 }
502 }
503 }
504
505 #[pallet::storage]
514 pub type XpOf<T: Config<I>, I: 'static = ()> =
515 StorageMap<_, Blake2_128Concat, XpId<T>, Xp<T, I>, OptionQuery>;
516
517 #[pallet::storage]
523 pub type XpOwners<T: Config<I>, I: 'static = ()> = StorageNMap<
524 _,
525 (
526 NMapKey<Blake2_128Concat, T::AccountId>,
527 NMapKey<Blake2_128Concat, XpId<T>>,
528 ),
529 (),
530 OptionQuery,
531 >;
532
533 #[pallet::storage]
542 pub type ReservedXpOf<T: Config<I>, I: 'static = ()> = StorageMap<
543 _,
544 Blake2_128Concat,
545 XpId<T>,
546 BoundedVec<IdXp<T::ReserveReason, T::Xp>, VariantCountOf<T::ReserveReason>>,
547 OptionQuery,
548 >;
549
550 #[pallet::storage]
559 pub type LockedXpOf<T: Config<I>, I: 'static = ()> = StorageMap<
560 _,
561 Blake2_128Concat,
562 XpId<T>,
563 BoundedVec<IdXp<T::LockReason, T::Xp>, VariantCountOf<T::LockReason>>,
564 OptionQuery,
565 >;
566
567 #[pallet::storage]
572 pub type ReapedXp<T: Config<I>, I: 'static = ()> =
573 StorageMap<_, Blake2_128Concat, XpId<T>, (), OptionQuery>;
574
575 #[pallet::storage]
581 pub type MinPulse<T: Config<I>, I: 'static = ()> = StorageValue<_, T::Pulse, ValueQuery>;
582
583 #[pallet::storage]
588 pub type InitXp<T: Config<I>, I: 'static = ()> = StorageValue<_, T::Xp, ValueQuery>;
589
590 #[pallet::storage]
595 pub type PulseFactor<T: Config<I>, I: 'static = ()> =
596 StorageValue<_, Stepper<T, I>, ValueQuery>;
597
598 #[pallet::storage]
604 pub type MinTimeStamp<T: Config<I>, I: 'static = ()> =
605 StorageValue<_, BlockNumberFor<T>, ValueQuery>;
606
607 #[pallet::error]
612 pub enum Error<T, I = ()> {
614 XpNotFound,
616 XpNotDead,
618 InvalidXpOwner,
620 AlreadyXpOwner,
622 CannotReapLockedXp,
624 XpLockExists,
626 CannotGenerateXpKey,
628 CannotTransferXp,
630 LowPulseThreshold,
632 InsufficientLiquidXp,
634 TooManyLocks,
636 TooManyReserves,
638 XpLockNotFound,
640 XpReserveNotFound,
642 InvalidMinTimeStamp,
644 LowTimeStamp,
646 XpNotReaped,
648 ReputationDeriveOverflowed,
651 XpCapOverflowed,
653 XpCapUnderflowed,
655 XpComputationError,
659 CannotLockZero,
661 CannotReserveZero,
663 XpAlreadyReaped,
665 InsufficientReserveXp,
667 XpReserveCapOverflowed,
669 XpReserveCapUnderflowed,
671 XpLockCapOverflowed,
673 XpLockCapUnderflowed,
675 }
676
677 #[pallet::event]
682 #[pallet::generate_deposit(pub(super) fn deposit_event)]
683 pub enum Event<T: Config<I>, I: 'static = ()> {
685 Xp { id: XpId<T>, xp: T::Xp },
687 XpOwner { id: XpId<T>, owner: T::AccountId },
689 XpOfOwner {
691 owner: T::AccountId,
692 ids: Vec<XpId<T>>,
693 },
694 XpEarn { id: XpId<T>, xp: T::Xp },
696 XpReap { id: XpId<T> },
698 XpSlash { id: XpId<T>, xp: T::Xp },
700 XpLock {
702 of: XpId<T>,
703 reason: T::LockReason,
704 xp: T::Xp,
705 },
706 XpLockBurn { of: XpId<T>, reason: T::LockReason },
708 XpLockSlash {
710 of: XpId<T>,
711 reason: T::LockReason,
712 xp: T::Xp,
713 },
714 XpReserve {
716 of: XpId<T>,
717 reason: T::ReserveReason,
718 xp: T::Xp,
719 },
720 XpReserveSlash {
722 of: XpId<T>,
723 reason: T::ReserveReason,
724 xp: T::Xp,
725 },
726 GenesisConfigUpdated(ForceGenesisConfig<T, I>),
728 }
729
730 #[pallet::call]
737 impl<T: Config<I>, I: 'static> Pallet<T, I> {
738 #[pallet::call_index(0)]
754 #[pallet::weight(T::WeightInfo::call())]
755 pub fn call(
756 origin: OriginFor<T>,
757 xp_id: XpId<T>,
758 call: Box<<T as Config<I>>::RuntimeCall>,
759 ) -> DispatchResult {
760 let caller = ensure_signed(origin)?;
761 Self::is_owner(&caller, &xp_id)?;
762 call.dispatch(frame_system::RawOrigin::Signed(xp_id).into())
763 .map(|_| ())
764 .map_err(|e| e.error)?;
765 Ok(())
766 }
767
768 #[pallet::call_index(1)]
781 #[pallet::weight(T::WeightInfo::handover())]
782 pub fn handover(
783 origin: OriginFor<T>,
784 xp_id: XpId<T>,
785 new_owner: T::AccountId,
786 ) -> DispatchResult {
787 let caller = ensure_signed(origin)?;
788 Self::xp_exists(&xp_id)?;
789 Self::is_owner(&caller, &xp_id)?;
790 ensure!(
791 caller != new_owner,
792 DispatchError::from(Error::<T, I>::AlreadyXpOwner)
793 );
794 Self::transfer_owner(&caller, &xp_id, &new_owner)?;
796 if !T::EmitEvents::get() {
798 Self::deposit_event(Event::XpOwner {
799 id: xp_id,
800 owner: new_owner,
801 });
802 }
803 Ok(())
804 }
805
806 #[pallet::call_index(2)]
822 #[pallet::weight(T::WeightInfo::dispose())]
823 pub fn dispose(
824 origin: OriginFor<T>,
825 owner: T::AccountId,
826 xp_id: XpId<T>,
827 ) -> DispatchResult {
828 let _caller = ensure_signed(origin)?;
829 Self::xp_exists(&xp_id)?;
830 Self::is_owner(&owner, &xp_id)?;
831 Self::try_reap(&xp_id)?;
832 if !T::EmitEvents::get() {
834 Self::deposit_event(Event::XpReap { id: xp_id.clone() });
835 }
836 Ok(())
837 }
838
839 #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
856 #[pallet::call_index(3)]
857 #[pallet::weight(T::WeightInfo::inspect_my_xp())]
858 pub fn inspect_my_xp(origin: OriginFor<T>, xp_id: XpId<T>) -> DispatchResult {
859 let caller = ensure_signed(origin)?;
860 Self::xp_exists(&xp_id)?;
861 Self::is_owner(&caller, &xp_id)?;
862 let liquid = Self::xp(&xp_id)?;
864 Self::deposit_event(Event::Xp {
866 id: xp_id.clone(),
867 xp: liquid,
868 });
869 Ok(())
870 }
871
872 #[cfg(any(feature = "dev", feature = "runtime-benchmarks"))]
884 #[pallet::call_index(4)]
885 #[pallet::weight(T::WeightInfo::inspect_xp_keys_of())]
886 pub fn inspect_xp_keys_of(origin: OriginFor<T>, owner: T::AccountId) -> DispatchResult {
887 let _caller = ensure_signed(origin)?;
888 let xp_ids = Self::xp_keys(&owner)?;
889 Self::deposit_event(Event::XpOfOwner {
890 owner: owner,
891 ids: xp_ids,
892 });
893 Ok(())
894 }
895
896 #[pallet::call_index(5)]
913 #[pallet::weight(T::WeightInfo::force_handover())]
914 pub fn force_handover(
915 origin: OriginFor<T>,
916 owner: T::AccountId,
917 xp_id: XpId<T>,
918 new_owner: T::AccountId,
919 ) -> DispatchResult {
920 ensure_root(origin)?;
921 Self::xp_exists(&xp_id)?;
922 Self::is_owner(&owner, &xp_id)?;
923 ensure!(
924 owner != new_owner,
925 DispatchError::from(Error::<T, I>::AlreadyXpOwner)
926 );
927 Self::transfer_owner(&owner, &xp_id, &new_owner)?;
929 if !T::EmitEvents::get() {
931 Self::deposit_event(Event::XpOwner {
932 id: xp_id.clone(),
933 owner: new_owner.clone(),
934 });
935 }
936 Ok(())
937 }
938
939 #[pallet::call_index(6)]
961 #[pallet::weight(
962 T::WeightInfo::force_update_init_xp()
963 .max(T::WeightInfo::force_update_min_pulse())
964 .max(T::WeightInfo::force_update_pulse_factor())
965 .max(T::WeightInfo::force_update_min_time_stamp())
966 )]
967 pub fn force_genesis_config(
968 origin: OriginFor<T>,
969 field: ForceGenesisConfig<T, I>,
970 ) -> DispatchResult {
971 ensure_root(origin)?;
972 match field {
973 ForceGenesisConfig::MinPulse(min_pulse) => MinPulse::<T, I>::set(min_pulse),
974 ForceGenesisConfig::InitXp(init_xp) => InitXp::<T, I>::set(init_xp),
975 ForceGenesisConfig::PulseFactor {
976 threshold,
977 per_count,
978 } => {
979 let Some(stepper) = Stepper::<T, I>::new(threshold, per_count) else {
980 return Err(Error::<T, I>::LowPulseThreshold.into());
981 };
982 PulseFactor::<T, I>::set(stepper);
983 }
984 ForceGenesisConfig::MinTimeStamp(min_block) => {
985 let current_block = frame_system::Pallet::<T>::block_number();
986 if min_block > current_block {
987 return Err(Error::<T, I>::InvalidMinTimeStamp.into());
988 };
989 MinTimeStamp::<T, I>::set(min_block);
990 }
991 }
992 Self::deposit_event(Event::GenesisConfigUpdated(field));
993 Ok(())
994 }
995 }
996
997 impl<T: Config<I>, I: 'static> Pallet<T, I> {
1010 pub fn xp_state(key: &XpId<T>) -> Result<XpState<T, I>, DispatchError> {
1016 let xp = Self::get_xp(key)?;
1017
1018 let eligibility = Self::xp_eligibility(key)?;
1019
1020 let required_pulse = MinPulse::<T, I>::get();
1021 let multiplier = match xp.pulse.value < required_pulse {
1022 true => One::one(),
1023 false => xp.pulse.value,
1024 };
1025
1026 Ok(XpState {
1027 liquid: xp.free,
1028 reserved: xp.reserve,
1029 locked: xp.lock,
1030 multiplier,
1031 eligibility,
1032 })
1033 }
1034
1035 pub fn xp(key: &XpId<T>) -> Result<T::Xp, DispatchError> {
1039 Self::xp_exists(key)?;
1040 let liquid = Self::get_liquid_xp(key)?;
1041 Ok(liquid)
1042 }
1043
1044 pub fn xp_keys(owner: &T::AccountId) -> Result<Vec<XpId<T>>, DispatchError> {
1046 let xp_ids = Self::xp_of_owner(owner)?;
1047 Ok(xp_ids)
1048 }
1049
1050 pub fn is_disposable(key: &XpId<T>) -> DispatchResult {
1052 Self::can_reap(key)?;
1053 Ok(())
1054 }
1055
1056 pub fn xp_eligibility(key: &XpId<T>) -> Result<XpEligibility<T, I>, DispatchError> {
1073 let xp = Self::get_xp(key)?;
1074 let current_pulse = xp.pulse.value;
1075 let current_progress = xp.pulse.step;
1076
1077 let required_pulse = MinPulse::<T, I>::get();
1078 let pulse_factor = PulseFactor::<T, I>::get();
1079
1080 if current_pulse >= required_pulse {
1082 return Ok(XpEligibility::Earning);
1083 }
1084
1085 let threshold = pulse_factor.threshold;
1086 let per_action = pulse_factor.per_count;
1087
1088 ensure!(!per_action.is_zero(), Error::<T, I>::XpComputationError);
1089
1090 let zero = T::Pulse::zero();
1091 let one = T::Pulse::one();
1092
1093 let ceil_div_pulse =
1094 |value: T::Pulse, by: T::Pulse| -> Result<T::Pulse, DispatchError> {
1095 ensure!(!by.is_zero(), Error::<T, I>::XpComputationError);
1096
1097 let adjusted = value.checked_sub(&one).unwrap_or(zero);
1098
1099 adjusted
1100 .checked_div(&by)
1101 .and_then(|v| v.checked_add(&one))
1102 .ok_or(Error::<T, I>::XpComputationError.into())
1103 };
1104
1105 let remaining_pulses = required_pulse
1107 .checked_sub(¤t_pulse)
1108 .ok_or(Error::<T, I>::XpComputationError)?;
1109
1110 let actions_per_pulse = ceil_div_pulse(threshold, per_action)?;
1112
1113 let remaining_progress = threshold.checked_sub(¤t_progress).unwrap_or(zero);
1115 let actions_to_next_pulse = ceil_div_pulse(remaining_progress, per_action)?;
1116
1117 let extra_pulses = remaining_pulses.checked_sub(&one).unwrap_or(zero);
1119
1120 let extra_actions = extra_pulses
1121 .checked_mul(&actions_per_pulse)
1122 .ok_or(Error::<T, I>::XpComputationError)?;
1123
1124 let total_actions = actions_to_next_pulse
1125 .checked_add(&extra_actions)
1126 .ok_or(Error::<T, I>::XpComputationError)?;
1127
1128 Ok(XpEligibility::Progressing(total_actions))
1129 }
1130
1131 pub fn xp_multiplier(key: &XpId<T>) -> Result<Option<T::Pulse>, DispatchError> {
1147 let xp = Self::get_xp(key)?;
1148 let required_pulse = MinPulse::<T, I>::get();
1149
1150 let multiplier = match xp.pulse.value < required_pulse {
1151 true => return Ok(None),
1153 false => xp.pulse.value,
1155 };
1156
1157 let current_block = frame_system::Pallet::<T>::block_number();
1158
1159 if xp.timestamp >= current_block {
1160 return Ok(None);
1161 }
1162
1163 Ok(Some(multiplier))
1164 }
1165
1166 pub fn xp_progress(key: &XpId<T>) -> Result<XpProgress<T, I>, DispatchError> {
1173 let xp = Self::get_xp(key)?;
1174 let config = PulseFactor::<T, I>::get();
1175
1176 Ok(XpProgress {
1177 level: xp.pulse.value,
1178 progress: xp.pulse.step,
1179 threshold: config.threshold,
1180 per_action: config.per_count,
1181 })
1182 }
1183
1184 pub fn earn_preview(key: &XpId<T>, raw: T::Xp) -> Result<XpState<T, I>, DispatchError> {
1201 let xp = Self::get_xp(key)?;
1202
1203 let reward = Self::quote_earn_xp(key, raw)?;
1205
1206 let new_free = xp
1208 .free
1209 .checked_add(&reward)
1210 .ok_or(Error::<T, I>::XpCapOverflowed)?;
1211
1212 let mut next_pulse = xp.pulse.clone();
1214 let config = PulseFactor::<T, I>::get();
1215
1216 <Pallet<T, I> as DiscreteAccumulator>::increment(&mut next_pulse, &config);
1217
1218 let next_key = key; let next_eligibility = match next_pulse.value >= MinPulse::<T, I>::get() {
1221 true => XpEligibility::Earning,
1222 false => Self::xp_eligibility(next_key)?,
1223 };
1224
1225 let next_multiplier = match next_eligibility {
1226 XpEligibility::Earning => next_pulse.value,
1227 _ => T::Pulse::one(),
1228 };
1229
1230 Ok(XpState {
1231 liquid: new_free,
1232 reserved: xp.reserve,
1233 locked: xp.lock,
1234 multiplier: next_multiplier,
1235 eligibility: next_eligibility,
1236 })
1237 }
1238
1239 pub fn xp_last_earn(key: &XpId<T>) -> Result<BlockNumberFor<T>, DispatchError> {
1247 let xp = Self::get_xp(key)?;
1248 Ok(xp.timestamp)
1249 }
1250 }
1251}
1252
1253#[cfg(test)]
1259mod ext_tests {
1260
1261 use crate::{
1267 mock::*,
1268 types::{ForceGenesisConfig, IdXp, XpEligibility},
1269 };
1270
1271 use frame_suite::xp::{XpLock, XpMutate, XpOwner, XpReserve, XpSystem};
1273
1274 use frame_support::{assert_err, assert_ok, traits::VariantCountOf};
1276
1277 use sp_runtime::{BoundedVec, DispatchError};
1279
1280 #[test]
1285 fn pulse_factor_instance_check() {
1286 xp_test_ext().execute_with(|| {
1287 let threshold_1 = 100;
1288 let per_count_1 = 10;
1289
1290 let threshold_2 = 1000;
1291 let per_count_2 = 100;
1292
1293 let old_pulsefactor_instance1 = PulseFactor::get();
1294 let old_pulsefactor_instance2 = PulseFactor2::get();
1295 assert_eq!(
1296 old_pulsefactor_instance1,
1297 Stepper::new(50u8.into(), 10u8.into()).unwrap(),
1298 );
1299 assert_eq!(
1300 old_pulsefactor_instance2,
1301 Stepper2::new(20u8.into(), 6u8.into()).unwrap(),
1302 );
1303
1304 let stepper_1 = Stepper::new(threshold_1, per_count_1).unwrap();
1305 let stepper_2 = Stepper2::new(threshold_2, per_count_2).unwrap();
1306
1307 PulseFactor::set(stepper_1.clone());
1308 PulseFactor2::set(stepper_2.clone());
1309
1310 assert_eq!(PulseFactor::get(), stepper_1);
1311 assert_eq!(PulseFactor2::get(), stepper_2);
1312 });
1313 }
1314
1315 #[test]
1316 fn min_pulse_instance_check() {
1317 xp_test_ext().execute_with(|| {
1318 let min_pulse_1 = 10;
1319 let min_pulse_2 = 15;
1320
1321 let old_minpulse_instance1 = MinPulse::get();
1322 let old_min_pulse_instance2 = MinPulse2::get();
1323 assert_eq!(old_minpulse_instance1, 1);
1324 assert_eq!(old_min_pulse_instance2, 5);
1325
1326 MinPulse::set(min_pulse_1);
1327 MinPulse2::set(min_pulse_2);
1328 assert_eq!(MinPulse::get(), 10);
1329 assert_eq!(MinPulse2::get(), 15);
1330 });
1331 }
1332
1333 #[test]
1334 fn init_xp_instance_check() {
1335 xp_test_ext().execute_with(|| {
1336 let init_xp_1 = 5;
1337 let init_xp_2 = 3;
1338
1339 let old_initxp_instance1 = InitXp::get();
1340 let old_initxp_instance2 = InitXp2::get();
1341 assert_eq!(old_initxp_instance1, 10);
1342 assert_eq!(old_initxp_instance2, 1);
1343
1344 InitXp::set(init_xp_1);
1345 InitXp2::set(init_xp_2);
1346 assert_eq!(InitXp::get(), 5);
1347 assert_eq!(InitXp2::get(), 3);
1348 });
1349 }
1350
1351 #[test]
1352 fn min_time_stamp_instance_check() {
1353 xp_test_ext().execute_with(|| {
1354 let min_time_stamp_1 = 5;
1355 let min_time_stamp_2 = 10;
1356
1357 let old_mintimestamp_instance1 = MinTimeStamp::get();
1358 let old_mintimestamp_instance2 = MinTimeStamp2::get();
1359 assert_eq!(old_mintimestamp_instance1, 0);
1360 assert_eq!(old_mintimestamp_instance2, 0);
1361
1362 MinTimeStamp::set(min_time_stamp_1);
1363 MinTimeStamp2::set(min_time_stamp_2);
1364 assert_eq!(MinTimeStamp::get(), 5);
1365 assert_eq!(MinTimeStamp2::get(), 10);
1366 });
1367 }
1368
1369 #[test]
1370 fn xp_of_instance_check() {
1371 xp_test_ext().execute_with(|| {
1372 let xp_1 = MockXp::default();
1373 XpOf::insert(XP_ALPHA, xp_1);
1374
1375 let xp_2 = MockXp2::default();
1376 XpOf2::insert(XP_BETA, xp_2);
1377
1378 assert!(XpOf::contains_key(XP_ALPHA));
1379 assert!(XpOf2::contains_key(XP_BETA));
1380
1381 assert!(!XpOf::contains_key(XP_BETA));
1382 assert!(!XpOf2::contains_key(XP_ALPHA));
1383 });
1384 }
1385
1386 #[test]
1387 fn xp_owners_instance_check() {
1388 xp_test_ext().execute_with(|| {
1389 XpOwners::insert((ALICE, XP_ALPHA), ());
1390
1391 XpOwners2::insert((BOB, XP_BETA), ());
1392
1393 assert!(XpOwners::contains_key((ALICE, XP_ALPHA)));
1394 assert!(XpOwners2::contains_key((BOB, XP_BETA)));
1395 assert!(!XpOwners::contains_key((BOB, XP_BETA)));
1396 assert!(!XpOwners2::contains_key((ALICE, XP_ALPHA)));
1397 });
1398 }
1399
1400 #[test]
1401 fn reserved_xp_of_instance_check() {
1402 xp_test_ext().execute_with(|| {
1403 let reserve_1 = IdXp::new(STAKING, DEFAULT_POINTS);
1404
1405 ReservedXpOf::try_mutate(XP_ALPHA, |value| {
1406 let vec = value.get_or_insert_with(|| {
1407 BoundedVec::<IdXp<Reason, u64>, VariantCountOf<Reason>>::default()
1408 });
1409 vec.try_push(reserve_1)
1410 })
1411 .unwrap();
1412
1413 let reserve_2 = IdXp::new(GOVERNANCE, DEFAULT_POINTS);
1414
1415 ReservedXpOf2::try_mutate(XP_BETA, |value| {
1416 let vec = value.get_or_insert_with(|| {
1417 BoundedVec::<IdXp<Reason, u64>, VariantCountOf<Reason>>::default()
1418 });
1419 vec.try_push(reserve_2)
1420 })
1421 .unwrap();
1422
1423 assert!(ReservedXpOf::contains_key(XP_ALPHA));
1424 assert!(ReservedXpOf2::contains_key(XP_BETA));
1425 assert!(!ReservedXpOf::contains_key(XP_BETA));
1426 assert!(!ReservedXpOf2::contains_key(XP_ALPHA));
1427 });
1428 }
1429
1430 #[test]
1431 fn locked_xp_of_instance_check() {
1432 xp_test_ext().execute_with(|| {
1433 let lock_1 = IdXp::new(STAKING, DEFAULT_POINTS);
1434
1435 LockedXpOf::try_mutate(XP_ALPHA, |value| {
1436 let vec = value.get_or_insert_with(|| {
1437 BoundedVec::<IdXp<Reason, u64>, VariantCountOf<Reason>>::default()
1438 });
1439 vec.try_push(lock_1)
1440 })
1441 .unwrap();
1442
1443 let lock_2 = IdXp::new(GOVERNANCE, DEFAULT_POINTS);
1444 LockedXpOf2::try_mutate(XP_BETA, |value| {
1445 let vec = value.get_or_insert_with(|| {
1446 BoundedVec::<IdXp<Reason, u64>, VariantCountOf<Reason>>::default()
1447 });
1448 vec.try_push(lock_2)
1449 })
1450 .unwrap();
1451
1452 assert!(LockedXpOf::contains_key(XP_ALPHA));
1453 assert!(LockedXpOf2::contains_key(XP_BETA));
1454 assert!(!LockedXpOf::contains_key(XP_BETA));
1455 assert!(!LockedXpOf2::contains_key(XP_ALPHA));
1456 });
1457 }
1458
1459 #[test]
1460 fn reaped_xp_instance_check() {
1461 xp_test_ext().execute_with(|| {
1462 ReapedXp::insert(XP_ALPHA, ());
1463 ReapedXp2::insert(XP_BETA, ());
1464
1465 assert!(ReapedXp::contains_key(XP_ALPHA));
1466 assert!(ReapedXp2::contains_key(XP_BETA));
1467
1468 assert!(!ReapedXp::contains_key(XP_BETA));
1469 assert!(!ReapedXp2::contains_key(XP_ALPHA));
1470 });
1471 }
1472
1473 #[test]
1478 fn xp_eligibility_success_already_reputed() {
1479 xp_test_ext().execute_with(|| {
1480 Pallet::new_xp(&ALICE, &XP_ALPHA);
1481 let xp = Pallet::get_xp(&XP_ALPHA).unwrap();
1482 let min_pulse = MinPulse::get();
1483 assert!(xp.pulse.value < min_pulse);
1484
1485 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1486 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1487 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1488 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1489 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1490
1491 let xp = Pallet::get_xp(&XP_ALPHA).unwrap();
1492 let min_pulse = MinPulse::get();
1493 assert!(xp.pulse.value >= min_pulse);
1494
1495 let status = Pallet::xp_eligibility(&XP_ALPHA).unwrap();
1496 assert_eq!(status, XpEligibility::Earning);
1497 })
1498 }
1499
1500 #[test]
1501 fn xp_eligibility_success_edge_cases() {
1502 xp_test_ext().execute_with(|| {
1503 Pallet::new_xp(&ALICE, &XP_ALPHA);
1504 let stepper = Stepper::new(20, 6).unwrap();
1508 PulseFactor::put(stepper);
1509
1510 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1512 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1513
1514 let xp = Pallet::get_xp(&XP_ALPHA).unwrap();
1515 let min_pulse = MinPulse::get();
1516 assert!(xp.pulse.value < min_pulse);
1517 assert_eq!(xp.pulse.step, 12);
1518
1519 let status = Pallet::xp_eligibility(&XP_ALPHA).unwrap();
1520 assert_eq!(status, XpEligibility::Progressing(2));
1521
1522 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1523 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1524
1525 let xp = Pallet::get_xp(&XP_ALPHA).unwrap();
1526 let min_pulse = MinPulse::get();
1527 assert!(xp.pulse.value >= min_pulse);
1528 assert_eq!(xp.pulse.step, 4);
1529
1530 let status = Pallet::xp_eligibility(&XP_ALPHA).unwrap();
1531 assert_eq!(status, XpEligibility::Earning);
1532 })
1533 }
1534
1535 #[test]
1536 fn xp_eligibility_success_calls_to_reach_reputed() {
1537 xp_test_ext().execute_with(|| {
1538 Pallet::new_xp(&ALICE, &XP_ALPHA);
1539 let xp = Pallet::get_xp(&XP_ALPHA).unwrap();
1540 let min_pulse = MinPulse::get();
1541 assert!(xp.pulse.value < min_pulse);
1542
1543 let status = Pallet::xp_eligibility(&XP_ALPHA).unwrap();
1544 assert_eq!(status, XpEligibility::Progressing(5));
1545
1546 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1547
1548 let status = Pallet::xp_eligibility(&XP_ALPHA).unwrap();
1549
1550 assert_eq!(status, XpEligibility::Progressing(4));
1551
1552 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1553 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1554
1555 let status = Pallet::xp_eligibility(&XP_ALPHA).unwrap();
1556
1557 assert_eq!(status, XpEligibility::Progressing(2));
1558
1559 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1560 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1561
1562 let xp = Pallet::get_xp(&XP_ALPHA).unwrap();
1563 let min_pulse = MinPulse::get();
1564 assert!(xp.pulse.value >= min_pulse);
1565
1566 let status = Pallet::xp_eligibility(&XP_ALPHA).unwrap();
1567 assert_eq!(status, XpEligibility::Earning);
1568 })
1569 }
1570
1571 #[test]
1572 fn xp_multiplier_less_than_min_pulse() {
1573 xp_test_ext().execute_with(|| {
1574 Pallet::new_xp(&ALICE, &XP_ALPHA);
1575
1576 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1577 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1578 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1579 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1580
1581 let xp = Pallet::get_xp(&XP_ALPHA).unwrap();
1582 let min_pulse = MinPulse::get();
1583 assert!(xp.pulse.value < min_pulse);
1584
1585 let current_multiplier = Pallet::xp_multiplier(&XP_ALPHA).unwrap();
1586 assert!(current_multiplier.is_none());
1587 })
1588 }
1589
1590 #[test]
1591 fn xp_multiplier_same_block_protection() {
1592 xp_test_ext().execute_with(|| {
1593 System::set_block_number(1);
1594 Pallet::new_xp(&ALICE, &XP_ALPHA);
1595 System::set_block_number(12);
1596 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1597 System::set_block_number(13);
1598 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1599 System::set_block_number(14);
1600 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1601 System::set_block_number(15);
1602 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1603 System::set_block_number(16);
1604 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1605 System::set_block_number(17);
1606 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1607
1608 let xp = Pallet::get_xp(&XP_ALPHA).unwrap();
1609 let min_pulse = MinPulse::get();
1610 assert!(xp.pulse.value == min_pulse);
1611 let current_multiplier = Pallet::xp_multiplier(&XP_ALPHA).unwrap();
1612 assert!(current_multiplier.is_none());
1613 })
1614 }
1615
1616 #[test]
1617 fn xp_multiplier_success() {
1618 xp_test_ext().execute_with(|| {
1619 System::set_block_number(1);
1620 Pallet::new_xp(&ALICE, &XP_ALPHA);
1621 System::set_block_number(12);
1622 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1623 System::set_block_number(13);
1624 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1625 System::set_block_number(14);
1626 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1627 System::set_block_number(15);
1628 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1629 System::set_block_number(16);
1630 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1631
1632 let xp = Pallet::get_xp(&XP_ALPHA).unwrap();
1633 let min_pulse = MinPulse::get();
1634 assert!(xp.pulse.value == min_pulse);
1635 System::set_block_number(17);
1636 let current_multiplier = Pallet::xp_multiplier(&XP_ALPHA).unwrap();
1637 assert_eq!(current_multiplier, Some(1));
1638
1639 Pallet::set_lock(&XP_ALPHA, &STAKING, DEFAULT_POINTS).unwrap();
1640 System::set_block_number(20);
1641 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1642 System::set_block_number(21);
1643 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1644 System::set_block_number(22);
1645 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1646 System::set_block_number(23);
1647 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1648 System::set_block_number(24);
1649 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1650
1651 let xp = Pallet::get_xp(&XP_ALPHA).unwrap();
1652 let min_pulse = MinPulse::get();
1653 assert!(xp.pulse.value > min_pulse);
1654 dbg!(xp.pulse.value);
1655 System::set_block_number(25);
1656 let current_multiplier = Pallet::xp_multiplier(&XP_ALPHA).unwrap();
1657 assert_eq!(current_multiplier, Some(2));
1658 })
1659 }
1660
1661 #[test]
1662 fn xp_state_success() {
1663 xp_test_ext().execute_with(|| {
1664 System::set_block_number(5);
1665 Pallet::new_xp(&ALICE, &XP_ALPHA);
1666
1667 System::set_block_number(20);
1668 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1669 System::set_block_number(21);
1670 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1671 System::set_block_number(22);
1672 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1673 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1674
1675 let xp_state = Pallet::xp_state(&XP_ALPHA).unwrap();
1676 assert_eq!(xp_state.liquid, 10);
1677 assert_eq!(xp_state.reserved, 0);
1678 assert_eq!(xp_state.locked, 0);
1679 assert_eq!(xp_state.multiplier, 1);
1680 assert_eq!(xp_state.eligibility, XpEligibility::Progressing(1));
1681
1682 System::set_block_number(23);
1683 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1684 Pallet::set_lock(&XP_ALPHA, &STAKING, DEFAULT_POINTS).unwrap();
1685 Pallet::set_reserve(&XP_ALPHA, &STAKING, 25).unwrap();
1686
1687 System::set_block_number(24);
1688 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1689
1690 let xp_state = Pallet::xp_state(&XP_ALPHA).unwrap();
1691 assert_eq!(xp_state.liquid, 20);
1692 assert_eq!(xp_state.reserved, 25);
1693 assert_eq!(xp_state.locked, DEFAULT_POINTS);
1694 assert_eq!(xp_state.multiplier, 1);
1695 assert_eq!(xp_state.eligibility, XpEligibility::Earning);
1696 })
1697 }
1698
1699 #[test]
1700 fn fetch_pulse_progress() {
1701 xp_test_ext().execute_with(|| {
1702 System::set_block_number(5);
1703 Pallet::new_xp(&ALICE, &XP_ALPHA);
1704
1705 System::set_block_number(20);
1706 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1707 System::set_block_number(21);
1708 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1709
1710 let pulse_progress = Pallet::xp_progress(&XP_ALPHA).unwrap();
1711 assert_eq!(pulse_progress.progress, 20);
1712 assert_eq!(pulse_progress.level, 0);
1713 assert_eq!(pulse_progress.threshold, 50);
1714 assert_eq!(pulse_progress.per_action, 10);
1715
1716 System::set_block_number(22);
1717 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1718 System::set_block_number(23);
1719 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1720 System::set_block_number(24);
1721 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1722
1723 let pulse_progress = Pallet::xp_progress(&XP_ALPHA).unwrap();
1724 assert_eq!(pulse_progress.progress, 0);
1725 assert_eq!(pulse_progress.level, 1);
1726 assert_eq!(pulse_progress.threshold, 50);
1727 assert_eq!(pulse_progress.per_action, 10);
1728 })
1729 }
1730
1731 #[test]
1732 fn earn_preview_below_min_pulse_returns_zero_reward_and_required_steps() {
1733 xp_test_ext().execute_with(|| {
1734 System::set_block_number(10);
1735 Pallet::new_xp(&ALICE, &XP_ALPHA);
1736
1737 let earn_preview = Pallet::earn_preview(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1738 assert_eq!(earn_preview.liquid, DEFAULT_POINTS);
1739 assert_eq!(earn_preview.reserved, 0);
1740 assert_eq!(earn_preview.locked, 0);
1741 assert_eq!(earn_preview.multiplier, 1);
1742 assert_eq!(earn_preview.eligibility, XpEligibility::Progressing(5));
1743 })
1744 }
1745
1746 #[test]
1747 fn earn_preview_will_repute_progress() {
1748 xp_test_ext().execute_with(|| {
1749 System::set_block_number(10);
1750 Pallet::new_xp(&ALICE, &XP_ALPHA);
1751
1752 System::set_block_number(22);
1753 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1754 System::set_block_number(23);
1755 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1756 System::set_block_number(24);
1757 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1758
1759 System::set_block_number(25);
1760 let earn_preview = Pallet::earn_preview(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1761 assert_eq!(earn_preview.liquid, DEFAULT_POINTS);
1762 assert_eq!(earn_preview.reserved, 0);
1763 assert_eq!(earn_preview.locked, 0);
1764 assert_eq!(earn_preview.multiplier, 1);
1765 assert_eq!(earn_preview.eligibility, XpEligibility::Progressing(2));
1766
1767 System::set_block_number(25);
1768 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1769
1770 System::set_block_number(26);
1771 let earn_preview = Pallet::earn_preview(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1772 assert_eq!(earn_preview.liquid, DEFAULT_POINTS);
1773 assert_eq!(earn_preview.reserved, 0);
1774 assert_eq!(earn_preview.locked, 0);
1775 assert_eq!(earn_preview.multiplier, 1);
1776 assert_eq!(earn_preview.eligibility, XpEligibility::Earning);
1777 })
1778 }
1779
1780 #[test]
1781 fn earn_preview_above_min_pulse() {
1782 xp_test_ext().execute_with(|| {
1783 System::set_block_number(10);
1784 Pallet::new_xp(&ALICE, &XP_ALPHA);
1785
1786 System::set_block_number(22);
1787 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1788 System::set_block_number(23);
1789 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1790 System::set_block_number(24);
1791 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1792 System::set_block_number(25);
1793 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1794 System::set_block_number(26);
1795 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1796
1797 System::set_block_number(27);
1798 let earn_preview = Pallet::earn_preview(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1799 assert_eq!(earn_preview.liquid, 20);
1800 assert_eq!(earn_preview.reserved, 0);
1801 assert_eq!(earn_preview.locked, 0);
1802 assert_eq!(earn_preview.multiplier, 1);
1803 assert_eq!(earn_preview.eligibility, XpEligibility::Earning);
1804 })
1805 }
1806
1807 #[test]
1808 fn earn_preview_multiplier_progress_without_lock() {
1809 xp_test_ext().execute_with(|| {
1810 System::set_block_number(10);
1811 Pallet::new_xp(&ALICE, &XP_ALPHA);
1812
1813 System::set_block_number(22);
1815 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1816 System::set_block_number(23);
1817 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1818 System::set_block_number(24);
1819 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1820 System::set_block_number(25);
1821 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1822 System::set_block_number(26);
1823 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1824
1825 System::set_block_number(27);
1826 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1827 let earn_preview = Pallet::earn_preview(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1829 assert_eq!(earn_preview.liquid, 30);
1830 assert_eq!(earn_preview.reserved, 0);
1831 assert_eq!(earn_preview.locked, 0);
1832 assert_eq!(earn_preview.multiplier, 1);
1833 assert_eq!(earn_preview.eligibility, XpEligibility::Earning);
1834
1835 for n in 28..48 {
1836 System::set_block_number(n);
1837 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1838 }
1839 let earn_preview = Pallet::earn_preview(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1841 assert_eq!(earn_preview.liquid, 230);
1842 assert_eq!(earn_preview.reserved, 0);
1843 assert_eq!(earn_preview.locked, 0);
1844 assert_eq!(earn_preview.multiplier, 1);
1845 assert_eq!(earn_preview.eligibility, XpEligibility::Earning);
1846 })
1847 }
1848
1849 #[test]
1850 fn earn_preview_with_lock() {
1851 xp_test_ext().execute_with(|| {
1852 System::set_block_number(10);
1853 Pallet::new_xp(&ALICE, &XP_ALPHA);
1854
1855 System::set_block_number(22);
1856 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1857 System::set_block_number(23);
1858 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1859 System::set_block_number(24);
1860 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1861 System::set_block_number(25);
1862 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1863 System::set_block_number(26);
1864 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1865
1866 Pallet::set_lock(&XP_ALPHA, &STAKING, DEFAULT_POINTS).unwrap();
1867
1868 System::set_block_number(27);
1869 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1870 System::set_block_number(28);
1871 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1872
1873 System::set_block_number(29);
1874 let earn_preview = Pallet::earn_preview(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1875 assert_eq!(earn_preview.liquid, 40);
1876 assert_eq!(earn_preview.reserved, 0);
1877 assert_eq!(earn_preview.locked, 10);
1878 assert_eq!(earn_preview.multiplier, 1);
1879 assert_eq!(earn_preview.eligibility, XpEligibility::Earning);
1880
1881 System::set_block_number(30);
1882 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1883 System::set_block_number(31);
1884 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1885
1886 System::set_block_number(32);
1887 let earn_preview = Pallet::earn_preview(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1888 assert_eq!(earn_preview.liquid, 60);
1889 assert_eq!(earn_preview.reserved, 0);
1890 assert_eq!(earn_preview.locked, 10);
1891 assert_eq!(earn_preview.multiplier, 2);
1892 assert_eq!(earn_preview.eligibility, XpEligibility::Earning);
1893 })
1894 }
1895
1896 #[test]
1897 fn earn_preview_with_lock_multiplier_progress() {
1898 xp_test_ext().execute_with(|| {
1899 System::set_block_number(10);
1900 Pallet::new_xp(&ALICE, &XP_ALPHA);
1901
1902 System::set_block_number(22);
1903 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1904 System::set_block_number(23);
1905 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1906 System::set_block_number(24);
1907 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1908 System::set_block_number(25);
1909 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1910 System::set_block_number(26);
1911 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1912
1913 Pallet::set_lock(&XP_ALPHA, &STAKING, DEFAULT_POINTS).unwrap();
1914
1915 System::set_block_number(27);
1916 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1917 System::set_block_number(28);
1918 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1919 System::set_block_number(29);
1920 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1921 System::set_block_number(30);
1922 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1923
1924 System::set_block_number(31);
1925 let earn_preview = Pallet::earn_preview(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1926 assert_eq!(earn_preview.liquid, 60);
1927 assert_eq!(earn_preview.reserved, 0);
1928 assert_eq!(earn_preview.locked, 10);
1929 assert_eq!(earn_preview.multiplier, 2);
1930 assert_eq!(earn_preview.eligibility, XpEligibility::Earning);
1931
1932 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1933
1934 for n in 32..42 {
1935 System::set_block_number(n);
1936 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1937 }
1938
1939 let earn_preview = Pallet::earn_preview(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1941 assert_eq!(earn_preview.liquid, 320);
1942 assert_eq!(earn_preview.reserved, 0);
1943 assert_eq!(earn_preview.locked, 10);
1944 assert_eq!(earn_preview.multiplier, 4);
1945 assert_eq!(earn_preview.eligibility, XpEligibility::Earning);
1946 })
1947 }
1948
1949 #[test]
1950 fn earn_preview_with_same_block_protection() {
1951 xp_test_ext().execute_with(|| {
1952 System::set_block_number(10);
1953 Pallet::new_xp(&ALICE, &XP_ALPHA);
1954
1955 System::set_block_number(22);
1956 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1957 System::set_block_number(23);
1958 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1959 System::set_block_number(24);
1960 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1961 System::set_block_number(25);
1962 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1963 System::set_block_number(26);
1964 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1965
1966 Pallet::set_lock(&XP_ALPHA, &STAKING, DEFAULT_POINTS).unwrap();
1967
1968 System::set_block_number(27);
1969 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1970 System::set_block_number(28);
1971 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1972 System::set_block_number(29);
1973 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1974 System::set_block_number(30);
1975 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1976 System::set_block_number(31);
1977 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1978
1979 let earn_preview = Pallet::earn_preview(&XP_ALPHA, DEFAULT_POINTS).unwrap();
1980 assert_eq!(earn_preview.liquid, 70);
1981 assert_eq!(earn_preview.reserved, 0);
1982 assert_eq!(earn_preview.locked, 10);
1983 assert_eq!(earn_preview.multiplier, 2);
1984 assert_eq!(earn_preview.eligibility, XpEligibility::Earning);
1985 })
1986 }
1987
1988 #[test]
1989 fn earn_preview_matches_earn_xp_actual_reward() {
1990 xp_test_ext().execute_with(|| {
1991 System::set_block_number(10);
1992 Pallet::new_xp(&ALICE, &XP_ALPHA);
1993
1994 Pallet::set_lock(&XP_ALPHA, &STAKING, DEFAULT_POINTS).unwrap();
1995
1996 for n in 20..40 {
1998 System::set_block_number(n);
1999 Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
2000 }
2001
2002 System::set_block_number(41);
2003
2004 let earn_preview = Pallet::earn_preview(&XP_ALPHA, DEFAULT_POINTS).unwrap();
2005 assert_eq!(earn_preview.liquid, 350);
2006 assert_eq!(earn_preview.reserved, 0);
2007 assert_eq!(earn_preview.locked, 10);
2008 assert_eq!(earn_preview.multiplier, 4);
2009 assert_eq!(earn_preview.eligibility, XpEligibility::Earning);
2010
2011 let xp = Pallet::get_xp(&XP_ALPHA).unwrap();
2012 let free_before = xp.free;
2013
2014 let actual_earn = Pallet::earn_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
2015 let xp = Pallet::get_xp(&XP_ALPHA).unwrap();
2016 let free_after = xp.free;
2017
2018 let diff = free_after - free_before;
2019 assert_eq!(free_after, earn_preview.liquid);
2020 assert_eq!(diff, actual_earn);
2021 })
2022 }
2023
2024 #[test]
2025 fn earn_preview_err_xp_not_found() {
2026 xp_test_ext().execute_with(|| {
2027 Pallet::new_xp(&ALICE, &XP_ALPHA);
2028 assert_err!(
2029 Pallet::earn_preview(&XP_BETA, DEFAULT_POINTS),
2030 Error::XpNotFound
2031 );
2032 })
2033 }
2034
2035 #[cfg(feature = "dev")]
2040 #[test]
2041 fn inspect_my_xp_success() {
2042 xp_test_ext().execute_with(|| {
2043 Pallet::new_xp(&ALICE, &XP_ALPHA);
2044 System::set_block_number(1);
2045 assert_ok!(Xp::inspect_my_xp(RuntimeOrigin::signed(ALICE), XP_ALPHA));
2046 System::assert_last_event(
2047 Event::Xp {
2048 id: XP_ALPHA,
2049 xp: InitXp::get(),
2050 }
2051 .into(),
2052 );
2053 });
2054 }
2055
2056 #[cfg(feature = "dev")]
2057 #[test]
2058 fn inspect_my_xp_fail_xp_not_found() {
2059 xp_test_ext().execute_with(|| {
2060 Pallet::new_xp(&ALICE, &XP_ALPHA);
2061 assert_err!(
2062 Xp::inspect_my_xp(RuntimeOrigin::signed(ALICE), XP_BETA),
2063 Error::XpNotFound
2064 );
2065 });
2066 }
2067
2068 #[cfg(feature = "dev")]
2069 #[test]
2070 fn inspect_my_xp_fail_not_signed() {
2071 xp_test_ext().execute_with(|| {
2072 assert_err!(
2073 Xp::inspect_my_xp(RuntimeOrigin::root(), XP_ALPHA),
2074 DispatchError::BadOrigin
2075 );
2076 });
2077 }
2078
2079 #[cfg(feature = "dev")]
2080 #[test]
2081 fn inspect_my_xp_fail_invalid_owner() {
2082 xp_test_ext().execute_with(|| {
2083 Pallet::new_xp(&ALICE, &XP_ALPHA);
2084 assert_err!(
2085 Xp::inspect_my_xp(RuntimeOrigin::signed(BOB), XP_ALPHA),
2086 Error::InvalidXpOwner
2087 );
2088 });
2089 }
2090
2091 #[test]
2092 fn handover_success() {
2093 xp_test_ext().execute_with(|| {
2094 Pallet::new_xp(&ALICE, &XP_ALPHA);
2095 System::set_block_number(1);
2096 assert_ok!(Xp::handover(RuntimeOrigin::signed(ALICE), XP_ALPHA, BOB));
2097 assert_ok!(Pallet::is_owner(&BOB, &XP_ALPHA));
2098 System::assert_last_event(
2099 Event::XpOwner {
2100 id: XP_ALPHA,
2101 owner: BOB,
2102 }
2103 .into(),
2104 );
2105 });
2106 }
2107
2108 #[test]
2109 fn handover_fail_xp_not_found() {
2110 xp_test_ext().execute_with(|| {
2111 assert_err!(
2112 Xp::handover(RuntimeOrigin::signed(ALICE), XP_ALPHA, BOB),
2113 Error::XpNotFound
2114 );
2115 });
2116 }
2117
2118 #[test]
2119 fn handover_fail_not_signed() {
2120 xp_test_ext().execute_with(|| {
2121 assert_err!(
2122 Xp::handover(RuntimeOrigin::root(), XP_ALPHA, BOB),
2123 DispatchError::BadOrigin
2124 );
2125 });
2126 }
2127
2128 #[test]
2129 fn handover_fail_invalid_owner() {
2130 xp_test_ext().execute_with(|| {
2131 Pallet::new_xp(&ALICE, &XP_ALPHA);
2132 assert_err!(
2133 Xp::handover(RuntimeOrigin::signed(CHARLIE), XP_ALPHA, BOB),
2134 Error::InvalidXpOwner
2135 );
2136 });
2137 }
2138
2139 #[test]
2140 fn handover_fail_already_owner() {
2141 xp_test_ext().execute_with(|| {
2142 Pallet::new_xp(&ALICE, &XP_ALPHA);
2143 assert_err!(
2144 Xp::handover(RuntimeOrigin::signed(ALICE), XP_ALPHA, ALICE),
2145 Error::AlreadyXpOwner
2146 );
2147 });
2148 }
2149
2150 #[test]
2151 fn dispose_success() {
2152 xp_test_ext().execute_with(|| {
2153 MinTimeStamp::set(3);
2154 System::set_block_number(1);
2155 Pallet::new_xp(&ALICE, &XP_ALPHA);
2156 Pallet::set_xp(&XP_ALPHA, 0).unwrap();
2157 assert_ok!(Pallet::xp_exists(&XP_ALPHA));
2158 System::set_block_number(2);
2159 assert_ok!(Xp::dispose(RuntimeOrigin::signed(CHARLIE), ALICE, XP_ALPHA));
2160 assert_err!(Pallet::xp_exists(&XP_ALPHA), Error::XpNotFound);
2161 });
2162 }
2163
2164 #[test]
2165 fn dispose_fail_xp_not_found() {
2166 xp_test_ext().execute_with(|| {
2167 Pallet::new_xp(&ALICE, &XP_ALPHA);
2168
2169 assert_err!(
2170 Xp::dispose(RuntimeOrigin::signed(CHARLIE), ALICE, XP_BETA),
2171 Error::XpNotFound
2172 );
2173 });
2174 }
2175
2176 #[test]
2177 fn dispose_fail_not_owner() {
2178 xp_test_ext().execute_with(|| {
2179 Pallet::new_xp(&ALICE, &XP_ALPHA);
2180 assert_err!(
2181 Xp::dispose(RuntimeOrigin::signed(CHARLIE), BOB, XP_ALPHA),
2182 Error::InvalidXpOwner
2183 );
2184 });
2185 }
2186
2187 #[test]
2188 fn dispose_fail_xp_not_dead() {
2189 xp_test_ext().execute_with(|| {
2190 System::set_block_number(1);
2191 System::set_block_number(2);
2192 System::set_block_number(3);
2193 Pallet::new_xp(&ALICE, &XP_ALPHA);
2194 Pallet::set_xp(&XP_ALPHA, DEFAULT_POINTS).unwrap();
2195 assert_err!(
2196 Xp::dispose(RuntimeOrigin::signed(CHARLIE), ALICE, XP_ALPHA),
2197 Error::XpNotDead
2198 );
2199 });
2200 }
2201
2202 #[test]
2203 fn dispose_fail_locked_xp() {
2204 xp_test_ext().execute_with(|| {
2205 MinTimeStamp::set(3);
2206 Pallet::new_xp(&ALICE, &XP_ALPHA);
2207 Pallet::set_lock(&XP_ALPHA, &STAKING, DEFAULT_POINTS).unwrap();
2208 System::set_block_number(2);
2209 assert_err!(
2210 Xp::dispose(RuntimeOrigin::signed(CHARLIE), ALICE, XP_ALPHA),
2211 Error::CannotReapLockedXp
2212 );
2213 });
2214 }
2215
2216 #[test]
2217 fn force_handover_success() {
2218 xp_test_ext().execute_with(|| {
2219 Pallet::new_xp(&ALICE, &XP_ALPHA);
2220 System::set_block_number(1);
2221 assert_ok!(Xp::force_handover(
2222 RuntimeOrigin::root(),
2223 ALICE,
2224 XP_ALPHA,
2225 BOB
2226 ));
2227 assert_ok!(Pallet::is_owner(&BOB, &XP_ALPHA));
2228 System::assert_last_event(
2229 Event::XpOwner {
2230 id: XP_ALPHA,
2231 owner: BOB,
2232 }
2233 .into(),
2234 );
2235 });
2236 }
2237
2238 #[test]
2239 fn force_handover_fail_xp_not_found() {
2240 xp_test_ext().execute_with(|| {
2241 Pallet::new_xp(&ALICE, &XP_ALPHA);
2242 assert_err!(
2243 Xp::force_handover(RuntimeOrigin::root(), ALICE, XP_BETA, BOB),
2244 Error::XpNotFound
2245 );
2246 });
2247 }
2248
2249 #[test]
2250 fn force_handover_fail_not_root() {
2251 xp_test_ext().execute_with(|| {
2252 Pallet::new_xp(&ALICE, &XP_ALPHA);
2253 assert_err!(
2254 Xp::force_handover(RuntimeOrigin::signed(CHARLIE), ALICE, XP_ALPHA, BOB),
2255 DispatchError::BadOrigin
2256 );
2257 });
2258 }
2259
2260 #[test]
2261 fn force_handover_fail_invalid_owner() {
2262 xp_test_ext().execute_with(|| {
2263 Pallet::new_xp(&ALICE, &XP_ALPHA);
2264 assert_err!(
2265 Xp::force_handover(RuntimeOrigin::root(), CHARLIE, XP_ALPHA, BOB),
2266 Error::InvalidXpOwner
2267 );
2268 });
2269 }
2270
2271 #[test]
2272 fn force_handover_fail_already_owner() {
2273 xp_test_ext().execute_with(|| {
2274 Pallet::new_xp(&ALICE, &XP_ALPHA);
2275 assert_err!(
2276 Xp::force_handover(RuntimeOrigin::root(), ALICE, XP_ALPHA, ALICE),
2277 Error::AlreadyXpOwner
2278 );
2279 });
2280 }
2281
2282 #[cfg(feature = "dev")]
2283 #[test]
2284 fn inspect_xp_keys_of_success() {
2285 xp_test_ext().execute_with(|| {
2286 Pallet::new_xp(&ALICE, &XP_ALPHA);
2287 Pallet::new_xp(&ALICE, &XP_BETA);
2288 System::set_block_number(1);
2289 assert_ok!(Xp::inspect_xp_keys_of(RuntimeOrigin::signed(ALICE), ALICE));
2290 System::assert_last_event(
2291 Event::XpOfOwner {
2292 owner: ALICE,
2293 ids: vec![XP_ALPHA, XP_BETA],
2294 }
2295 .into(),
2296 );
2297 });
2298 }
2299
2300 #[cfg(feature = "dev")]
2301 #[test]
2302 fn inspect_xp_keys_of_fail_not_signed() {
2303 xp_test_ext().execute_with(|| {
2304 Pallet::new_xp(&ALICE, &XP_ALPHA);
2305 Pallet::new_xp(&ALICE, &XP_BETA);
2306 assert_err!(
2307 Xp::inspect_xp_keys_of(RuntimeOrigin::root(), ALICE),
2308 DispatchError::BadOrigin
2309 );
2310 });
2311 }
2312
2313 #[test]
2314 fn force_genesis_config_min_pulse_success() {
2315 xp_test_ext().execute_with(|| {
2316 System::set_block_number(1);
2317 let new_min_pulse: u32 = 5;
2318 assert_ok!(Xp::force_genesis_config(
2319 RuntimeOrigin::root(),
2320 ForceGenesisConfig::MinPulse(new_min_pulse)
2321 ));
2322 assert_eq!(MinPulse::get(), new_min_pulse);
2323
2324 System::assert_last_event(
2325 Event::GenesisConfigUpdated(ForceGenesisConfig::MinPulse(new_min_pulse)).into(),
2326 );
2327 });
2328 }
2329
2330 #[test]
2331 fn force_genesis_config_min_pulse_fail_not_root() {
2332 xp_test_ext().execute_with(|| {
2333 let min_pulse = 5;
2334 assert_err!(
2335 Xp::force_genesis_config(
2336 RuntimeOrigin::signed(CHARLIE),
2337 ForceGenesisConfig::MinPulse(min_pulse)
2338 ),
2339 DispatchError::BadOrigin
2340 );
2341 assert_eq!(MinPulse::get(), 1);
2342 });
2343 }
2344
2345 #[test]
2346 fn force_genesis_config_init_xp_success() {
2347 xp_test_ext().execute_with(|| {
2348 System::set_block_number(1);
2349 let new_init_xp = 50;
2350 assert_ok!(Xp::force_genesis_config(
2351 RuntimeOrigin::root(),
2352 ForceGenesisConfig::InitXp(new_init_xp)
2353 ));
2354 assert_eq!(InitXp::get(), new_init_xp);
2355 System::assert_last_event(
2356 Event::GenesisConfigUpdated(ForceGenesisConfig::InitXp(new_init_xp)).into(),
2357 );
2358 });
2359 }
2360
2361 #[test]
2362 fn force_genesis_config_init_xp_fail_not_root() {
2363 xp_test_ext().execute_with(|| {
2364 let new_init_xp = 50;
2365 assert_err!(
2366 Xp::force_genesis_config(
2367 RuntimeOrigin::signed(CHARLIE),
2368 ForceGenesisConfig::InitXp(new_init_xp)
2369 ),
2370 DispatchError::BadOrigin
2371 );
2372 assert_eq!(InitXp::get(), 10);
2373 });
2374 }
2375
2376 #[test]
2377 fn force_genesis_config_min_time_stamp_success() {
2378 xp_test_ext().execute_with(|| {
2379 System::set_block_number(1);
2380 let new_min_time_stamp = 4;
2381 System::set_block_number(5);
2382 assert_ok!(Xp::force_genesis_config(
2383 RuntimeOrigin::root(),
2384 ForceGenesisConfig::MinTimeStamp(new_min_time_stamp)
2385 ));
2386 assert_eq!(MinTimeStamp::get(), new_min_time_stamp);
2387 System::assert_last_event(
2388 Event::GenesisConfigUpdated(ForceGenesisConfig::MinTimeStamp(new_min_time_stamp))
2389 .into(),
2390 );
2391 });
2392 }
2393
2394 #[test]
2395 fn force_genesis_config_min_time_stamp_fail_not_root() {
2396 xp_test_ext().execute_with(|| {
2397 let new_min_time_stamp = 4;
2398 assert_err!(
2399 Xp::force_genesis_config(
2400 RuntimeOrigin::signed(ALICE),
2401 ForceGenesisConfig::MinTimeStamp(new_min_time_stamp)
2402 ),
2403 DispatchError::BadOrigin
2404 );
2405 });
2406 }
2407
2408 #[test]
2409 fn force_genesis_config_min_time_stamp_fail_invalid_min_time_stamp() {
2410 xp_test_ext().execute_with(|| {
2411 let new_min_time_stamp = 4;
2412 System::set_block_number(3);
2414 assert_err!(
2415 Xp::force_genesis_config(
2416 RuntimeOrigin::root(),
2417 ForceGenesisConfig::MinTimeStamp(new_min_time_stamp)
2418 ),
2419 Error::InvalidMinTimeStamp
2420 );
2421 });
2422 }
2423
2424 #[test]
2425 fn force_genesis_config_pulse_factor_success() {
2426 xp_test_ext().execute_with(|| {
2427 System::set_block_number(1);
2428 let threshold = 100;
2429 let per_count = 10;
2430 assert_ok!(Xp::force_genesis_config(
2431 RuntimeOrigin::root(),
2432 ForceGenesisConfig::PulseFactor {
2433 threshold,
2434 per_count
2435 }
2436 ));
2437 let stepper = PulseFactor::get();
2438 assert_eq!(stepper.threshold, threshold);
2439 assert_eq!(stepper.per_count, per_count);
2440 System::assert_last_event(
2441 Event::GenesisConfigUpdated(ForceGenesisConfig::PulseFactor {
2442 threshold,
2443 per_count,
2444 })
2445 .into(),
2446 );
2447 })
2448 }
2449
2450 #[test]
2451 fn force_genesis_config_pulse_factor_fail_low_pulse_threshold() {
2452 xp_test_ext().execute_with(|| {
2453 let threshold = 100;
2454 let per_count = 110;
2455 assert_err!(
2456 Xp::force_genesis_config(
2457 RuntimeOrigin::root(),
2458 ForceGenesisConfig::PulseFactor {
2459 threshold,
2460 per_count
2461 }
2462 ),
2463 Error::LowPulseThreshold
2464 );
2465 });
2466 }
2467
2468 #[test]
2469 fn force_genesis_config_pulse_factor_fail_not_root() {
2470 xp_test_ext().execute_with(|| {
2471 let threshold = 100;
2472 let per_count = 10;
2473 assert_err!(
2474 Xp::force_genesis_config(
2475 RuntimeOrigin::signed(ALICE),
2476 ForceGenesisConfig::PulseFactor {
2477 threshold,
2478 per_count
2479 }
2480 ),
2481 DispatchError::BadOrigin
2482 );
2483 });
2484 }
2485
2486 #[test]
2487 fn call_success() {
2488 xp_test_ext().execute_with(|| {
2489 Pallet::new_xp(&ALICE, &XP_ALPHA);
2490 Pallet::new_xp(&BOB, &XP_BETA);
2491
2492 let call = Box::new(Call::Xp(crate::Call::handover {
2493 xp_id: XP_ALPHA,
2494 new_owner: BOB,
2495 }));
2496 assert_ok!(Pallet::is_owner(&ALICE, &XP_ALPHA));
2497 System::set_block_number(2);
2498 assert_ok!(Xp::call(RuntimeOrigin::signed(ALICE), XP_ALPHA, call));
2499 assert_err!(Pallet::is_owner(&ALICE, &XP_ALPHA), Error::InvalidXpOwner);
2500 assert_ok!(Pallet::is_owner(&BOB, &XP_ALPHA));
2501 System::assert_last_event(
2502 Event::XpOwner {
2503 id: XP_ALPHA,
2504 owner: BOB,
2505 }
2506 .into(),
2507 );
2508 });
2509 }
2510
2511 #[test]
2512 fn call_fail_invalid_owner() {
2513 xp_test_ext().execute_with(|| {
2514 Pallet::new_xp(&ALICE, &XP_ALPHA);
2515 Pallet::new_xp(&BOB, &XP_BETA);
2516
2517 let call = Box::new(Call::Xp(crate::Call::handover {
2518 xp_id: XP_ALPHA,
2519 new_owner: BOB,
2520 }));
2521 assert_ok!(Pallet::is_owner(&ALICE, &XP_ALPHA));
2522 assert_err!(
2523 Xp::call(RuntimeOrigin::signed(ALICE), XP_BETA, call),
2524 Error::InvalidXpOwner
2525 );
2526 });
2527 }
2528
2529 #[test]
2530 fn call_fail_bad_origin() {
2531 xp_test_ext().execute_with(|| {
2532 Pallet::new_xp(&ALICE, &XP_ALPHA);
2533 Pallet::new_xp(&BOB, &XP_BETA);
2534
2535 let call = Box::new(Call::Xp(crate::Call::handover {
2536 xp_id: XP_ALPHA,
2537 new_owner: BOB,
2538 }));
2539 assert_ok!(Pallet::is_owner(&ALICE, &XP_ALPHA));
2540 assert_err!(
2541 Xp::call(RuntimeOrigin::root(), XP_ALPHA, call),
2542 DispatchError::BadOrigin
2543 );
2544 });
2545 }
2546}