git.delta.rocks / unique-network / refs/commits / 0f7bb4adb180

difftreelog

Merge pull request #341 from UniqueNetwork/feature/simple-scheduler

kozyrevdev2022-06-03parents: #93f5452 #9e5b125.patch.diff
in: master
Feature/simple scheduler

11 files changed

modifiedpallets/scheduler/Cargo.tomldiffbeforeafterboth
--- a/pallets/scheduler/Cargo.toml
+++ b/pallets/scheduler/Cargo.toml
@@ -15,11 +15,13 @@
 scale-info = { version = "2.0.1", default-features = false, features = [
 	"derive",
 ] }
+
 frame-support = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.22" }
 frame-system = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.22" }
 sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.22" }
 sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.22" }
 sp-io = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.22" }
+sp-core = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.22' }
 frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.22" }
 
 up-sponsorship = { version = "0.1.0", default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = "polkadot-v0.9.22" }
@@ -40,6 +42,7 @@
 	"up-sponsorship/std",
 	"sp-io/std",
 	"sp-std/std",
+	"sp-core/std",
 	"log/std",
 ]
 runtime-benchmarks = [
modifiedpallets/scheduler/src/lib.rsdiffbeforeafterboth
--- a/pallets/scheduler/src/lib.rs
+++ b/pallets/scheduler/src/lib.rs
@@ -1,23 +1,6 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-// Original license
 // This file is part of Substrate.
 
-// Copyright (C) 2017-2021 Parity Technologies (UK) Ltd.
+// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd.
 // SPDX-License-Identifier: Apache-2.0
 
 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -32,16 +15,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-//! # Scheduler
-//! A module for scheduling dispatches.
-//!
-//! - [`Config`]
-//! - [`Call`]
-//! - [`Module`]
-//!
-//! ## Overview
+//! # Schedulerdo_reschedule
 //!
-//! This module exposes capabilities for scheduling dispatches to occur at a
+//! This Pallet exposes capabilities for scheduling dispatches to occur at a
 //! specified block number or at a specified period. These scheduled dispatches
 //! may be named or anonymous and may be canceled.
 //!
@@ -57,106 +33,59 @@
 //!
 //! ### Dispatchable Functions
 //!
-//! * `schedule` - schedule a dispatch, which may be periodic, to occur at a
-//!   specified block and with a specified priority.
-//! * `cancel` - cancel a scheduled dispatch, specified by block number and
-//!   index.
-//! * `schedule_named` - augments the `schedule` interface with an additional
-//!   `Vec<u8>` parameter that can be used for identification.
+//! * `schedule` - schedule a dispatch, which may be periodic, to occur at a specified block and
+//!   with a specified priority.
+//! * `cancel` - cancel a scheduled dispatch, specified by block number and index.
+//! * `schedule_named` - augments the `schedule` interface with an additional `Vec<u8>` parameter
+//!   that can be used for identification.
 //! * `cancel_named` - the named complement to the cancel function.
 
 // Ensure we're `no_std` when compiling for Wasm.
 #![cfg_attr(not(feature = "std"), no_std)]
-#![allow(clippy::type_complexity, clippy::boxed_local, clippy::unused_unit)]
 
+#[cfg(feature = "runtime-benchmarks")]
 mod benchmarking;
+
 pub mod weights;
 
-use sp_std::{prelude::*, marker::PhantomData, borrow::Borrow};
-use codec::{Encode, Decode, Codec};
+use sp_core::H160;
+use codec::{Codec, Decode, Encode};
+use frame_system::{self as system, ensure_signed};
+pub use pallet::*;
+use scale_info::TypeInfo;
 use sp_runtime::{
-	RuntimeDebug,
-	traits::{Zero, One, BadOrigin, Saturating},
+	traits::{BadOrigin, One, Saturating, Zero},
+	RuntimeDebug, DispatchErrorWithPostInfo,
 };
+use sp_std::{borrow::Borrow, cmp::Ordering, marker::PhantomData, prelude::*};
+
 use frame_support::{
-	decl_module, decl_storage, decl_event, decl_error,
-	dispatch::{Dispatchable, DispatchError, DispatchResult, Parameter},
+	dispatch::{DispatchError, DispatchResult, Dispatchable, Parameter},
 	traits::{
-		Get,
-		schedule::{self, DispatchTime},
-		OriginTrait, EnsureOrigin, IsType,
+		schedule::{self, DispatchTime, MaybeHashed},
+		NamedReservableCurrency, EnsureOrigin, Get, IsType, OriginTrait, PrivilegeCmp,
+		StorageVersion,
 	},
 	weights::{GetDispatchInfo, Weight},
 };
-use frame_system::{self as system, ensure_signed};
-pub use weights::WeightInfo;
-use up_sponsorship::SponsorshipHandler;
-use scale_info::TypeInfo;
 
-/// Our pallet's configuration trait. All our types and constants go in here. If the
-/// pallet is dependent on specific other pallets, then their configuration traits
-/// should be added to our implied traits list.
-///
-/// `system::Config` should always be included in our implied traits.
-/// //
-pub trait Config: system::Config {
-	/// The overarching event type.
-	type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;
-
-	/// The aggregated origin which the dispatch will take.
-	type Origin: OriginTrait<PalletsOrigin = Self::PalletsOrigin>
-		+ From<Self::PalletsOrigin>
-		+ IsType<<Self as system::Config>::Origin>;
-
-	/// The caller origin, overarching type of all pallets origins.
-	type PalletsOrigin: From<system::RawOrigin<Self::AccountId>> + Codec + TypeInfo + Clone + Eq;
-
-	/// The aggregated call type.
-	type Call: Parameter
-		+ Dispatchable<Origin = <Self as Config>::Origin>
-		+ GetDispatchInfo
-		+ From<system::Call<Self>>;
-
-	/// The maximum weight that may be scheduled per block for any dispatchables of less priority
-	/// than `schedule::HARD_DEADLINE`.
-	type MaximumWeight: Get<Weight>;
-
-	/// Required origin to schedule or cancel calls.
-	type ScheduleOrigin: EnsureOrigin<<Self as system::Config>::Origin>;
-
-	/// The maximum number of scheduled calls in the queue for a single block.
-	/// Not strictly enforced, but used for weight estimation.
-	type MaxScheduledPerBlock: Get<u32>;
-
-	/// Sponsoring function
-	type SponsorshipHandler: SponsorshipHandler<Self::AccountId, <Self as Config>::Call>;
-
-	/// Weight information for extrinsics in this pallet.
-	type WeightInfo: WeightInfo;
-}
+pub use weights::WeightInfo;
 
-// pub type SelfWeightInfo<T> = <T as system::Config>::WeightInfo;
-
 /// Just a simple index for naming period tasks.
 pub type PeriodicIndex = u32;
 /// The location of a scheduled task that can be used to remove it.
 pub type TaskAddress<BlockNumber> = (BlockNumber, u32);
+pub const MAX_TASK_ID_LENGTH_IN_BYTES: u8 = 16;
 
-#[cfg_attr(any(feature = "std", test), derive(PartialEq, Eq))]
-#[derive(Clone, RuntimeDebug, Encode, Decode)]
-struct ScheduledV1<Call, BlockNumber> {
-	maybe_id: Option<Vec<u8>>,
-	priority: schedule::Priority,
-	call: Call,
-	maybe_periodic: Option<schedule::Period<BlockNumber>>,
-}
+type ScheduledId = [u8; MAX_TASK_ID_LENGTH_IN_BYTES as usize];
+pub type CallOrHashOf<T> = MaybeHashed<<T as Config>::Call, <T as frame_system::Config>::Hash>;
 
 /// Information regarding an item to be executed in the future.
 #[cfg_attr(any(feature = "std", test), derive(PartialEq, Eq))]
 #[derive(Clone, RuntimeDebug, Encode, Decode, TypeInfo)]
-pub struct ScheduledV2<Call, BlockNumber, PalletsOrigin, AccountId> {
+pub struct ScheduledV3<Call, BlockNumber, PalletsOrigin, AccountId> {
 	/// The unique identity for this task, if there is one.
-	maybe_id: Option<Vec<u8>>,
+	maybe_id: Option<ScheduledId>,
 	/// This task's priority.
 	priority: schedule::Priority,
 	/// The call to be dispatched.
@@ -168,63 +97,213 @@
 	_phantom: PhantomData<AccountId>,
 }
 
+pub type ScheduledV3Of<T> = ScheduledV3<
+	CallOrHashOf<T>,
+	<T as frame_system::Config>::BlockNumber,
+	<T as Config>::PalletsOrigin,
+	<T as frame_system::Config>::AccountId,
+>;
+
+pub type ScheduledOf<T> = ScheduledV3Of<T>;
+
 /// The current version of Scheduled struct.
 pub type Scheduled<Call, BlockNumber, PalletsOrigin, AccountId> =
-	ScheduledV2<Call, BlockNumber, PalletsOrigin, AccountId>;
+	ScheduledV3<Call, BlockNumber, PalletsOrigin, AccountId>;
 
-// A value placed in storage that represents the current version of the Scheduler storage.
-// This value is used by the `on_runtime_upgrade` logic to determine whether we run
-// storage migration logic.
-#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)]
-enum Releases {
-	V1,
-	V2,
+#[cfg(feature = "runtime-benchmarks")]
+mod preimage_provider {
+	use frame_support::traits::PreimageRecipient;
+	pub trait PreimageProviderAndMaybeRecipient<H>: PreimageRecipient<H> {}
+	impl<H, T: PreimageRecipient<H>> PreimageProviderAndMaybeRecipient<H> for T {}
 }
 
-impl Default for Releases {
-	fn default() -> Self {
-		Releases::V1
-	}
+#[cfg(not(feature = "runtime-benchmarks"))]
+mod preimage_provider {
+	use frame_support::traits::PreimageProvider;
+	pub trait PreimageProviderAndMaybeRecipient<H>: PreimageProvider<H> {}
+	impl<H, T: PreimageProvider<H>> PreimageProviderAndMaybeRecipient<H> for T {}
 }
 
-#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
-pub struct CallSpec {
-	module: u32,
-	method: u32,
+pub use preimage_provider::PreimageProviderAndMaybeRecipient;
+
+pub(crate) trait MarginalWeightInfo: WeightInfo {
+	fn item(periodic: bool, named: bool, resolved: Option<bool>) -> Weight {
+		match (periodic, named, resolved) {
+			(_, false, None) => Self::on_initialize_aborted(2) - Self::on_initialize_aborted(1),
+			(_, true, None) => {
+				Self::on_initialize_named_aborted(2) - Self::on_initialize_named_aborted(1)
+			}
+			(false, false, Some(false)) => Self::on_initialize(2) - Self::on_initialize(1),
+			(false, true, Some(false)) => {
+				Self::on_initialize_named(2) - Self::on_initialize_named(1)
+			}
+			(true, false, Some(false)) => {
+				Self::on_initialize_periodic(2) - Self::on_initialize_periodic(1)
+			}
+			(true, true, Some(false)) => {
+				Self::on_initialize_periodic_named(2) - Self::on_initialize_periodic_named(1)
+			}
+			(false, false, Some(true)) => {
+				Self::on_initialize_resolved(2) - Self::on_initialize_resolved(1)
+			}
+			(false, true, Some(true)) => {
+				Self::on_initialize_named_resolved(2) - Self::on_initialize_named_resolved(1)
+			}
+			(true, false, Some(true)) => {
+				Self::on_initialize_periodic_resolved(2) - Self::on_initialize_periodic_resolved(1)
+			}
+			(true, true, Some(true)) => {
+				Self::on_initialize_periodic_named_resolved(2)
+					- Self::on_initialize_periodic_named_resolved(1)
+			}
+		}
+	}
 }
+impl<T: WeightInfo> MarginalWeightInfo for T {}
 
-decl_storage! {
-	trait Store for Module<T: Config> as Scheduler {
-		/// Items to be executed, indexed by the block number that they should be executed on.
-		pub Agenda: map hasher(twox_64_concat) T::BlockNumber
-			=> Vec<Option<Scheduled<<T as Config>::Call, T::BlockNumber, T::PalletsOrigin, T::AccountId>>>;
+#[frame_support::pallet]
+pub mod pallet {
+	use super::*;
+	use frame_support::{
+		dispatch::PostDispatchInfo,
+		pallet_prelude::*,
+		traits::{schedule::LookupError, PreimageProvider},
+	};
+	use frame_system::pallet_prelude::*;
+
+	/// The current storage version.
+	const STORAGE_VERSION: StorageVersion = StorageVersion::new(3);
+
+	#[pallet::pallet]
+	#[pallet::generate_store(pub(super) trait Store)]
+	#[pallet::storage_version(STORAGE_VERSION)]
+	#[pallet::without_storage_info]
+	pub struct Pallet<T>(_);
+
+	/// `system::Config` should always be included in our implied traits.
+	#[pallet::config]
+	pub trait Config: frame_system::Config {
+		/// The overarching event type.
+		type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
+
+		/// The aggregated origin which the dispatch will take.
+		type Origin: OriginTrait<PalletsOrigin = Self::PalletsOrigin>
+			+ From<Self::PalletsOrigin>
+			+ IsType<<Self as system::Config>::Origin>;
+
+		/// The caller origin, overarching type of all pallets origins.
+		type PalletsOrigin: From<system::RawOrigin<Self::AccountId>> + Codec + Clone + Eq + TypeInfo;
+
+		type Currency: NamedReservableCurrency<Self::AccountId, ReserveIdentifier = ScheduledId>;
+
+		/// The aggregated call type.
+		type Call: Parameter
+			+ Dispatchable<Origin = <Self as Config>::Origin, PostInfo = PostDispatchInfo>
+			+ GetDispatchInfo
+			+ From<system::Call<Self>>;
 
-		pub SpecAgenda: map hasher(twox_64_concat) T::BlockNumber
-			=> Vec<Option<CallSpec>>;
+		/// The maximum weight that may be scheduled per block for any dispatchables of less
+		/// priority than `schedule::HARD_DEADLINE`.
+		#[pallet::constant]
+		type MaximumWeight: Get<Weight>;
 
-		/// Lookup from identity to the block number and index of the task.
-		Lookup: map hasher(twox_64_concat) Vec<u8> => Option<TaskAddress<T::BlockNumber>>;
+		/// Required origin to schedule or cancel calls.
+		type ScheduleOrigin: EnsureOrigin<<Self as system::Config>::Origin>;
 
-		/// Storage version of the pallet.
+		/// Compare the privileges of origins.
+		///
+		/// This will be used when canceling a task, to ensure that the origin that tries
+		/// to cancel has greater or equal privileges as the origin that created the scheduled task.
 		///
-		/// New networks start with last version.
-		StorageVersion build(|_| Releases::V2): Releases;
+		/// For simplicity the [`EqualPrivilegeOnly`](frame_support::traits::EqualPrivilegeOnly) can
+		/// be used. This will only check if two given origins are equal.
+		type OriginPrivilegeCmp: PrivilegeCmp<Self::PalletsOrigin>;
+
+		/// The maximum number of scheduled calls in the queue for a single block.
+		/// Not strictly enforced, but used for weight estimation.
+		#[pallet::constant]
+		type MaxScheduledPerBlock: Get<u32>;
+
+		/// Weight information for extrinsics in this pallet.
+		type WeightInfo: WeightInfo;
+
+		/// The preimage provider with which we look up call hashes to get the call.
+		type PreimageProvider: PreimageProviderAndMaybeRecipient<Self::Hash>;
+
+		/// If `Some` then the number of blocks to postpone execution for when the item is delayed.
+		type NoPreimagePostponement: Get<Option<Self::BlockNumber>>;
+
+		/// Sponsoring function.
+		// type SponsorshipHandler: SponsorshipHandler<Self::AccountId, <Self as Config>::Call>;
+
+		/// The helper type used for custom transaction fee logic.
+		type CallExecutor: DispatchCall<Self, H160>;
 	}
-}
 
-decl_event!(
-	pub enum Event<T> where <T as system::Config>::BlockNumber {
-		/// Scheduled some task. \[when, index\]
-		Scheduled(BlockNumber, u32),
-		/// Canceled some task. \[when, index\]
-		Canceled(BlockNumber, u32),
-		/// Dispatched some task. \[task, id, result\]
-		Dispatched(TaskAddress<BlockNumber>, Option<Vec<u8>>, DispatchResult),
+	/// A Scheduler-Runtime interface for finer payment handling.
+	pub trait DispatchCall<T: frame_system::Config + Config, SelfContainedSignedInfo> {
+		fn reserve_balance(
+			id: ScheduledId,
+			sponsor: <T as frame_system::Config>::AccountId,
+			call: <T as Config>::Call,
+			count: u32,
+		) -> Result<(), DispatchError>;
+
+		fn pay_for_call(
+			id: ScheduledId,
+			sponsor: <T as frame_system::Config>::AccountId,
+			call: <T as Config>::Call,
+		) -> Result<u128, DispatchError>;
+
+		/// Resolve the call dispatch, including any post-dispatch operations.
+		fn dispatch_call(
+			signer: T::AccountId,
+			function: <T as Config>::Call,
+		) -> Result<
+			Result<PostDispatchInfo, DispatchErrorWithPostInfo<PostDispatchInfo>>,
+			TransactionValidityError,
+		>;
+
+		fn cancel_reserve(
+			id: ScheduledId,
+			sponsor: <T as frame_system::Config>::AccountId,
+		) -> Result<u128, DispatchError>;
 	}
-);
 
-decl_error! {
-	pub enum Error for Module<T: Config> {
+	/// Items to be executed, indexed by the block number that they should be executed on.
+	#[pallet::storage]
+	pub type Agenda<T: Config> =
+		StorageMap<_, Twox64Concat, T::BlockNumber, Vec<Option<ScheduledV3Of<T>>>, ValueQuery>;
+
+	/// Lookup from identity to the block number and index of the task.
+	#[pallet::storage]
+	pub(crate) type Lookup<T: Config> =
+		StorageMap<_, Twox64Concat, ScheduledId, TaskAddress<T::BlockNumber>>;
+
+	/// Events type.
+	#[pallet::event]
+	#[pallet::generate_deposit(pub(super) fn deposit_event)]
+	pub enum Event<T: Config> {
+		/// Scheduled some task.
+		Scheduled { when: T::BlockNumber, index: u32 },
+		/// Canceled some task.
+		Canceled { when: T::BlockNumber, index: u32 },
+		/// Dispatched some task.
+		Dispatched {
+			task: TaskAddress<T::BlockNumber>,
+			id: Option<ScheduledId>,
+			result: DispatchResult,
+		},
+		/// The call for the provided hash was not found so the task has been aborted.
+		CallLookupFailed {
+			task: TaskAddress<T::BlockNumber>,
+			id: Option<ScheduledId>,
+			error: LookupError,
+		},
+	}
+
+	#[pallet::error]
+	pub enum Error<T> {
 		/// Failed to schedule a call
 		FailedToSchedule,
 		/// Cannot find the scheduled call.
@@ -234,249 +313,268 @@
 		/// Reschedule failed because it does not change scheduled time.
 		RescheduleNoChange,
 	}
-}
 
-decl_module! {
-	/// Scheduler module declaration.
-	pub struct Module<T: Config> for enum Call
-	where
-		origin: <T as system::Config>::Origin
-	{
-		type Error = Error<T>;
-		fn deposit_event() = default;
+	#[pallet::hooks]
+	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
+		/// Execute the scheduled calls
+		fn on_initialize(now: T::BlockNumber) -> Weight {
+			let limit = T::MaximumWeight::get();
 
+			let mut queued = Agenda::<T>::take(now)
+				.into_iter()
+				.enumerate()
+				.filter_map(|(index, s)| Some((index as u32, s?)))
+				.collect::<Vec<_>>();
 
-		/// Anonymously schedule a task.
-		///
-		/// # <weight>
-		/// - S = Number of already scheduled calls
-		/// - Base Weight: 22.29 + .126 * S µs
-		/// - DB Weight:
-		///     - Read: Agenda
-		///     - Write: Agenda
-		/// - Will use base weight of 25 which should be good for up to 30 scheduled calls
-		/// # </weight>
-		#[weight = <T as Config>::WeightInfo::schedule(T::MaxScheduledPerBlock::get())]
-		fn schedule(origin,
-			when: T::BlockNumber,
-			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,
-			priority: schedule::Priority,
-			call: Box<<T as Config>::Call>,
-		)
-		{
-			let origin = <T as Config>::Origin::from(origin);
-			Self::do_schedule(DispatchTime::At(when), maybe_periodic, priority, origin.caller().clone(), *call)?;
-		}
+			if queued.len() as u32 > T::MaxScheduledPerBlock::get() {
+				log::warn!(
+					target: "runtime::scheduler",
+					"Warning: This block has more items queued in Scheduler than \
+					expected from the runtime configuration. An update might be needed."
+				);
+			}
 
-		/// Cancel an anonymously scheduled task.
-		///
-		/// # <weight>
-		/// - S = Number of already scheduled calls
-		/// - Base Weight: 22.15 + 2.869 * S µs
-		/// - DB Weight:
-		///     - Read: Agenda
-		///     - Write: Agenda, Lookup
-		/// - Will use base weight of 100 which should be good for up to 30 scheduled calls
-		/// # </weight>
-		#[weight = <T as Config>::WeightInfo::cancel(T::MaxScheduledPerBlock::get())]
-		fn cancel(origin, when: T::BlockNumber, index: u32) {
-			T::ScheduleOrigin::ensure_origin(origin.clone())?;
-			let origin = <T as Config>::Origin::from(origin);
-			Self::do_cancel(Some(origin.caller().clone()), (when, index))?;
+			queued.sort_by_key(|(_, s)| s.priority);
+
+			let next = now + One::one();
+
+			let mut total_weight: Weight = T::WeightInfo::on_initialize(0);
+			for (order, (index, mut s)) in queued.into_iter().enumerate() {
+				let named = if let Some(ref id) = s.maybe_id {
+					Lookup::<T>::remove(id);
+					true
+				} else {
+					false
+				};
+
+				let (call, maybe_completed) = s.call.resolved::<T::PreimageProvider>();
+				s.call = call;
+
+				let resolved = if let Some(completed) = maybe_completed {
+					T::PreimageProvider::unrequest_preimage(&completed);
+					true
+				} else {
+					false
+				};
+				let call = match s.call.as_value().cloned() {
+					Some(c) => c,
+					None => {
+						// Preimage not available - postpone until some block.
+						total_weight.saturating_accrue(T::WeightInfo::item(false, named, None));
+						if let Some(delay) = T::NoPreimagePostponement::get() {
+							let until = now.saturating_add(delay);
+							if let Some(ref id) = s.maybe_id {
+								let index = Agenda::<T>::decode_len(until).unwrap_or(0);
+								Lookup::<T>::insert(id, (until, index as u32));
+							}
+							Agenda::<T>::append(until, Some(s));
+						}
+						continue;
+					}
+				};
+
+				let periodic = s.maybe_periodic.is_some();
+				let call_weight = call.get_dispatch_info().weight;
+				let mut item_weight = T::WeightInfo::item(periodic, named, Some(resolved));
+				let origin =
+					<<T as Config>::Origin as From<T::PalletsOrigin>>::from(s.origin.clone())
+						.into();
+				if ensure_signed(origin).is_ok() {
+					// Weights of Signed dispatches expect their signing account to be whitelisted.
+					item_weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
+				}
+
+				// We allow a scheduled call if any is true:
+				// - It's priority is `HARD_DEADLINE`
+				// - It does not push the weight past the limit.
+				// - It is the first item in the schedule
+				let hard_deadline = s.priority <= schedule::HARD_DEADLINE;
+				let test_weight = total_weight
+					.saturating_add(call_weight)
+					.saturating_add(item_weight);
+				if !hard_deadline && order > 0 && test_weight > limit {
+					// Cannot be scheduled this block - postpone until next.
+					total_weight.saturating_accrue(T::WeightInfo::item(false, named, None));
+					if let Some(ref id) = s.maybe_id {
+						// NOTE: We could reasonably not do this (in which case there would be one
+						// block where the named and delayed item could not be referenced by name),
+						// but we will do it anyway since it should be mostly free in terms of
+						// weight and it is slightly cleaner.
+						let index = Agenda::<T>::decode_len(next).unwrap_or(0);
+						Lookup::<T>::insert(id, (next, index as u32));
+					}
+					Agenda::<T>::append(next, Some(s));
+					continue;
+				}
+
+				let sender = ensure_signed(
+					<<T as Config>::Origin as From<T::PalletsOrigin>>::from(s.origin.clone())
+						.into(),
+				)
+				.unwrap();
+
+				// // if call have id it was be reserved
+				// if s.maybe_id.is_some() {
+				// 	let _ = T::CallExecutor::pay_for_call(
+				// 		s.maybe_id.unwrap(),
+				// 		sender.clone(),
+				// 		call.clone(),
+				// 	);
+				// }
+
+				let r = T::CallExecutor::dispatch_call(sender, call.clone());
+
+				let mut actual_call_weight: Weight = item_weight;
+				let result: Result<_, DispatchError> = match r {
+					Ok(o) => match o {
+						Ok(di) => {
+							actual_call_weight = di.actual_weight.unwrap_or(item_weight);
+							Ok(())
+						}
+						Err(err) => Err(err.error),
+					},
+					Err(_) => {
+						log::error!(
+							target: "runtime::scheduler",
+							"Warning: Scheduler has failed to execute a post-dispatch transaction. \
+							This block might have become invalid.");
+						Err(DispatchError::CannotLookup)
+					} // todo possibly force a skip/return here, do something with the error
+				};
+
+				total_weight.saturating_accrue(item_weight);
+				total_weight.saturating_accrue(actual_call_weight);
+
+				Self::deposit_event(Event::Dispatched {
+					task: (now, index),
+					id: s.maybe_id.clone(),
+					result,
+				});
+
+				if let &Some((period, count)) = &s.maybe_periodic {
+					if count > 1 {
+						s.maybe_periodic = Some((period, count - 1));
+					} else {
+						s.maybe_periodic = None;
+					}
+					let wake = now + period;
+					// If scheduled is named, place its information in `Lookup`
+					if let Some(ref id) = s.maybe_id {
+						let wake_index = Agenda::<T>::decode_len(wake).unwrap_or(0);
+						Lookup::<T>::insert(id, (wake, wake_index as u32));
+					}
+					Agenda::<T>::append(wake, Some(s));
+				}
+			}
+			0
+			//total_weight
 		}
+	}
 
+	#[pallet::call]
+	impl<T: Config> Pallet<T> {
 		/// Schedule a named task.
-		///
-		/// # <weight>
-		/// - S = Number of already scheduled calls
-		/// - Base Weight: 29.6 + .159 * S µs
-		/// - DB Weight:
-		///     - Read: Agenda, Lookup
-		///     - Write: Agenda, Lookup
-		/// - Will use base weight of 35 which should be good for more than 30 scheduled calls
-		/// # </weight>
-		#[weight = <T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get())]
-		fn schedule_named(origin,
-			id: Vec<u8>,
+		#[pallet::weight(<T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get()))]
+		pub fn schedule_named(
+			origin: OriginFor<T>,
+			id: ScheduledId,
 			when: T::BlockNumber,
 			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,
 			priority: schedule::Priority,
-			call: Box<<T as Config>::Call>,
-		) {
+			call: Box<CallOrHashOf<T>>,
+		) -> DispatchResult {
 			T::ScheduleOrigin::ensure_origin(origin.clone())?;
 			let origin = <T as Config>::Origin::from(origin);
 			Self::do_schedule_named(
-				id, DispatchTime::At(when), maybe_periodic, priority, origin.caller().clone(), *call
+				id,
+				DispatchTime::At(when),
+				maybe_periodic,
+				priority,
+				origin.caller().clone(),
+				*call,
 			)?;
+			Ok(())
 		}
 
 		/// Cancel a named scheduled task.
-		///
-		/// # <weight>
-		/// - S = Number of already scheduled calls
-		/// - Base Weight: 24.91 + 2.907 * S µs
-		/// - DB Weight:
-		///     - Read: Agenda, Lookup
-		///     - Write: Agenda, Lookup
-		/// - Will use base weight of 100 which should be good for up to 30 scheduled calls
-		/// # </weight>
-		#[weight = <T as Config>::WeightInfo::cancel_named(T::MaxScheduledPerBlock::get())]
-		fn cancel_named(origin, id: Vec<u8>) {
+		#[pallet::weight(<T as Config>::WeightInfo::cancel_named(T::MaxScheduledPerBlock::get()))]
+		pub fn cancel_named(origin: OriginFor<T>, id: ScheduledId) -> DispatchResult {
 			T::ScheduleOrigin::ensure_origin(origin.clone())?;
 			let origin = <T as Config>::Origin::from(origin);
 			Self::do_cancel_named(Some(origin.caller().clone()), id)?;
-		}
-
-		/// Anonymously schedule a task after a delay.
-		///
-		/// # <weight>
-		/// Same as [`schedule`].
-		/// # </weight>
-		#[weight = <T as Config>::WeightInfo::schedule(T::MaxScheduledPerBlock::get())]
-		fn schedule_after(origin,
-			after: T::BlockNumber,
-			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,
-			priority: schedule::Priority,
-			call: Box<<T as Config>::Call>,
-		) {
-			T::ScheduleOrigin::ensure_origin(origin.clone())?;
-			let origin = <T as Config>::Origin::from(origin);
-			Self::do_schedule(
-				DispatchTime::After(after), maybe_periodic, priority, origin.caller().clone(), *call
-			)?;
+			Ok(())
 		}
 
 		/// Schedule a named task after a delay.
 		///
 		/// # <weight>
-		/// Same as [`schedule_named`].
+		/// Same as [`schedule_named`](Self::schedule_named).
 		/// # </weight>
-		#[weight = <T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get())]
-		fn schedule_named_after(origin,
-			id: Vec<u8>,
+		#[pallet::weight(<T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get()))]
+		pub fn schedule_named_after(
+			origin: OriginFor<T>,
+			id: ScheduledId,
 			after: T::BlockNumber,
 			maybe_periodic: Option<schedule::Period<T::BlockNumber>>,
 			priority: schedule::Priority,
-			call: Box<<T as Config>::Call>,
-		) {
+			call: Box<CallOrHashOf<T>>,
+		) -> DispatchResult {
 			T::ScheduleOrigin::ensure_origin(origin.clone())?;
 			let origin = <T as Config>::Origin::from(origin);
 			Self::do_schedule_named(
-				id, DispatchTime::After(after), maybe_periodic, priority, origin.caller().clone(), *call
+				id,
+				DispatchTime::After(after),
+				maybe_periodic,
+				priority,
+				origin.caller().clone(),
+				*call,
 			)?;
+			Ok(())
 		}
-
-		/// Execute the scheduled calls
-		///
-		/// # <weight>
-		/// - S = Number of already scheduled calls
-		/// - N = Named scheduled calls
-		/// - P = Periodic Calls
-		/// - Base Weight: 9.243 + 23.45 * S µs
-		/// - DB Weight:
-		///     - Read: Agenda + Lookup * N + Agenda(Future) * P
-		///     - Write: Agenda + Lookup * N  + Agenda(future) * P
-		/// # </weight>
-		fn on_initialize(now: T::BlockNumber) -> Weight {
-			let limit = T::MaximumWeight::get();
-			let mut queued = Agenda::<T>::take(now).into_iter()
-				.enumerate()
-				.filter_map(|(index, s)| s.map(|inner| (index as u32, inner)))
-				.collect::<Vec<_>>();
-			if queued.len() as u32 > T::MaxScheduledPerBlock::get() {
-				log::warn!(
-					target: "runtime::scheduler",
-					"Warning: This block has more items queued in Scheduler than \
-					expected from the runtime configuration. An update might be needed."
-				);
-			}
-			queued.sort_by_key(|(_, s)| s.priority);
-			let base_weight: Weight = T::DbWeight::get().reads_writes(1, 2); // Agenda + Agenda(next)
-			let mut total_weight: Weight = 0;
-			queued.into_iter()
-				.enumerate()
-				.scan(base_weight, |cumulative_weight, (order, (index, s))| {
-					*cumulative_weight = cumulative_weight
-						.saturating_add(s.call.get_dispatch_info().weight);
-
-					let origin = <<T as Config>::Origin as From<T::PalletsOrigin>>::from(
-						s.origin.clone()
-					).into();
-
-					if ensure_signed(origin).is_ok() {
-						 // AccountData for inner call origin accountdata.
-						*cumulative_weight = cumulative_weight
-							.saturating_add(T::DbWeight::get().reads_writes(1, 1));
-					}
-
-					if s.maybe_id.is_some() {
-						// Remove/Modify Lookup
-						*cumulative_weight = cumulative_weight.saturating_add(T::DbWeight::get().writes(1));
-					}
-					if s.maybe_periodic.is_some() {
-						// Read/Write Agenda for future block
-						*cumulative_weight = cumulative_weight.saturating_add(T::DbWeight::get().reads_writes(1, 1));
-					}
+	}
+}
 
-					Some((order, index, *cumulative_weight, s))
-				})
-				.filter_map(|(order, index, cumulative_weight, mut s)| {
-					// We allow a scheduled call if any is true:
-					// - It's priority is `HARD_DEADLINE`
-					// - It does not push the weight past the limit.
-					// - It is the first item in the schedule
-					if s.priority <= schedule::HARD_DEADLINE || cumulative_weight <= limit || order == 0 {
+impl<T: Config> Pallet<T> {
+	#[cfg(feature = "try-runtime")]
+	pub fn pre_migrate_to_v3() -> Result<(), &'static str> {
+		Ok(())
+	}
 
-						let origin = <<T as Config>::Origin as From<T::PalletsOrigin>>::from(
-							s.origin.clone()
-						).into();
-						let sender = match ensure_signed(origin) {
-							Ok(v) => v,
-							// TODO: Support for unsigned extrinsics?
-							Err(_) => return Some(Some(s))
-						};
-						let who_will_pay = T::SponsorshipHandler::get_sponsor(&sender, &s.call).unwrap_or(sender);
-						let sponsor = T::PalletsOrigin::from(system::RawOrigin::Signed(who_will_pay));
-						let r = s.call.clone().dispatch(sponsor.into());
-						let maybe_id = s.maybe_id.clone();
-						if let Some((period, count)) = s.maybe_periodic {
-							if count > 1 {
-								s.maybe_periodic = Some((period, count - 1));
-							} else {
-								s.maybe_periodic = None;
-							}
-							let next = now + period;
-							// If scheduled is named, place it's information in `Lookup`
-							if let Some(ref id) = s.maybe_id {
-								let next_index = Agenda::<T>::decode_len(now + period).unwrap_or(0);
-								Lookup::<T>::insert(id, (next, next_index as u32));
-							}
-							Agenda::<T>::append(next, Some(s));
-						} else if let Some(ref id) = s.maybe_id {
-									  Lookup::<T>::remove(id);
-								  }
-						Self::deposit_event(RawEvent::Dispatched(
-							(now, index),
-							maybe_id,
-							r.map(|_| ()).map_err(|e| e.error)
-						));
-						total_weight = cumulative_weight;
-						None
-					} else {
-						Some(Some(s))
-					}
-				})
-				.for_each(|unused| {
-					let next = now + One::one();
-					Agenda::<T>::append(next, unused);
-				});
+	#[cfg(feature = "try-runtime")]
+	pub fn post_migrate_to_v3() -> Result<(), &'static str> {
+		use frame_support::dispatch::GetStorageVersion;
 
-			total_weight
+		assert!(Self::current_storage_version() == 3);
+		for k in Agenda::<T>::iter_keys() {
+			let _ = Agenda::<T>::try_get(k).map_err(|()| "Invalid item in Agenda")?;
 		}
+		Ok(())
+	}
+
+	/// Helper to migrate scheduler when the pallet origin type has changed.
+	pub fn migrate_origin<OldOrigin: Into<T::PalletsOrigin> + codec::Decode>() {
+		Agenda::<T>::translate::<
+			Vec<Option<Scheduled<CallOrHashOf<T>, T::BlockNumber, OldOrigin, T::AccountId>>>,
+			_,
+		>(|_, agenda| {
+			Some(
+				agenda
+					.into_iter()
+					.map(|schedule| {
+						schedule.map(|schedule| Scheduled {
+							maybe_id: schedule.maybe_id,
+							priority: schedule.priority,
+							call: schedule.call,
+							maybe_periodic: schedule.maybe_periodic,
+							origin: schedule.origin.into(),
+							_phantom: Default::default(),
+						})
+					})
+					.collect::<Vec<_>>(),
+			)
+		});
 	}
-}
 
-impl<T: Config> Module<T> {
 	fn resolve_time(when: DispatchTime<T::BlockNumber>) -> Result<T::BlockNumber, DispatchError> {
 		let now = frame_system::Pallet::<T>::block_number();
 
@@ -499,9 +597,10 @@
 		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,
 		priority: schedule::Priority,
 		origin: T::PalletsOrigin,
-		call: <T as Config>::Call,
+		call: CallOrHashOf<T>,
 	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {
 		let when = Self::resolve_time(when)?;
+		call.ensure_requested::<T::PreimageProvider>();
 
 		// sanitize maybe_periodic
 		let maybe_periodic = maybe_periodic
@@ -518,14 +617,7 @@
 		});
 		Agenda::<T>::append(when, s);
 		let index = Agenda::<T>::decode_len(when).unwrap_or(1) as u32 - 1;
-		if index > T::MaxScheduledPerBlock::get() {
-			log::warn!(
-				target: "runtime::scheduler",
-				"Warning: There are more items queued in the Scheduler than \
-				expected from the runtime configuration. An update might be needed.",
-			);
-		}
-		Self::deposit_event(RawEvent::Scheduled(when, index));
+		Self::deposit_event(Event::Scheduled { when, index });
 
 		Ok((when, index))
 	}
@@ -539,7 +631,10 @@
 				Ok(None),
 				|s| -> Result<Option<Scheduled<_, _, _, _>>, DispatchError> {
 					if let (Some(ref o), Some(ref s)) = (origin, s.borrow()) {
-						if *o != s.origin {
+						if matches!(
+							T::OriginPrivilegeCmp::cmp_privilege(o, &s.origin),
+							Some(Ordering::Less) | None
+						) {
 							return Err(BadOrigin.into());
 						}
 					};
@@ -548,13 +643,14 @@
 			)
 		})?;
 		if let Some(s) = scheduled {
+			s.call.ensure_unrequested::<T::PreimageProvider>();
 			if let Some(id) = s.maybe_id {
 				Lookup::<T>::remove(id);
 			}
-			Self::deposit_event(RawEvent::Canceled(when, index));
+			Self::deposit_event(Event::Canceled { when, index });
 			Ok(())
 		} else {
-			Err(Error::<T>::NotFound.into())
+			Err(Error::<T>::NotFound)?
 		}
 	}
 
@@ -576,27 +672,32 @@
 		})?;
 
 		let new_index = Agenda::<T>::decode_len(new_time).unwrap_or(1) as u32 - 1;
-		Self::deposit_event(RawEvent::Canceled(when, index));
-		Self::deposit_event(RawEvent::Scheduled(new_time, new_index));
+		Self::deposit_event(Event::Canceled { when, index });
+		Self::deposit_event(Event::Scheduled {
+			when: new_time,
+			index: new_index,
+		});
 
 		Ok((new_time, new_index))
 	}
 
 	fn do_schedule_named(
-		id: Vec<u8>,
+		id: ScheduledId,
 		when: DispatchTime<T::BlockNumber>,
 		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,
 		priority: schedule::Priority,
 		origin: T::PalletsOrigin,
-		call: <T as Config>::Call,
+		call: CallOrHashOf<T>,
 	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {
 		// ensure id it is unique
 		if Lookup::<T>::contains_key(&id) {
-			return Err(Error::<T>::FailedToSchedule.into());
+			return Err(Error::<T>::FailedToSchedule)?;
 		}
 
 		let when = Self::resolve_time(when)?;
 
+		call.ensure_requested::<T::PreimageProvider>();
+
 		// sanitize maybe_periodic
 		let maybe_periodic = maybe_periodic
 			.filter(|p| p.1 > 1 && !p.0.is_zero())
@@ -606,52 +707,74 @@
 		let s = Scheduled {
 			maybe_id: Some(id.clone()),
 			priority,
-			call,
+			call: call.clone(),
 			maybe_periodic,
-			origin,
+			origin: origin.clone(),
 			_phantom: Default::default(),
 		};
+
+		// reserve balance for periodic execution
+		// let sender =
+		// 	ensure_signed(<<T as Config>::Origin as From<T::PalletsOrigin>>::from(origin).into())?;
+		// let repeats = match maybe_periodic {
+		// 	Some(p) => p.1,
+		// 	None => 1,
+		// };
+		// let _ = T::CallExecutor::reserve_balance(
+		// 	id.clone(),
+		// 	sender,
+		// 	call.as_value().unwrap().clone(),
+		// 	repeats,
+		// );
+
 		Agenda::<T>::append(when, Some(s));
 		let index = Agenda::<T>::decode_len(when).unwrap_or(1) as u32 - 1;
-		if index > T::MaxScheduledPerBlock::get() {
-			log::warn!(
-				target: "runtime::scheduler",
-				"Warning: There are more items queued in the Scheduler than \
-				expected from the runtime configuration. An update might be needed.",
-			);
-		}
 		let address = (when, index);
 		Lookup::<T>::insert(&id, &address);
-		Self::deposit_event(RawEvent::Scheduled(when, index));
+		Self::deposit_event(Event::Scheduled { when, index });
 
 		Ok(address)
 	}
 
-	fn do_cancel_named(origin: Option<T::PalletsOrigin>, id: Vec<u8>) -> DispatchResult {
+	fn do_cancel_named(origin: Option<T::PalletsOrigin>, id: ScheduledId) -> DispatchResult {
 		Lookup::<T>::try_mutate_exists(id, |lookup| -> DispatchResult {
 			if let Some((when, index)) = lookup.take() {
 				let i = index as usize;
 				Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult {
 					if let Some(s) = agenda.get_mut(i) {
-						if let (Some(ref o), Some(ref s)) = (origin, s.borrow()) {
-							if *o != s.origin {
+						if let (Some(ref o), Some(ref s)) = (origin.clone(), s.borrow()) {
+							if matches!(
+								T::OriginPrivilegeCmp::cmp_privilege(o, &s.origin),
+								Some(Ordering::Less) | None
+							) {
 								return Err(BadOrigin.into());
 							}
+							// release balance reserve
+							// let sender = ensure_signed(
+							// 	<<T as Config>::Origin as From<T::PalletsOrigin>>::from(
+							// 		origin.unwrap(),
+							// 	)
+							// 	.into(),
+							// )?;
+							// let _ = T::CallExecutor::cancel_reserve(id, sender);
+
+							s.call.ensure_unrequested::<T::PreimageProvider>();
 						}
 						*s = None;
 					}
 					Ok(())
 				})?;
-				Self::deposit_event(RawEvent::Canceled(when, index));
+
+				Self::deposit_event(Event::Canceled { when, index });
 				Ok(())
 			} else {
-				Err(Error::<T>::NotFound.into())
+				Err(Error::<T>::NotFound)?
 			}
 		})
 	}
 
 	fn do_reschedule_named(
-		id: Vec<u8>,
+		id: ScheduledId,
 		new_time: DispatchTime<T::BlockNumber>,
 	) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {
 		let new_time = Self::resolve_time(new_time)?;
@@ -674,8 +797,11 @@
 				})?;
 
 				let new_index = Agenda::<T>::decode_len(new_time).unwrap_or(1) as u32 - 1;
-				Self::deposit_event(RawEvent::Canceled(when, index));
-				Self::deposit_event(RawEvent::Scheduled(new_time, new_index));
+				Self::deposit_event(Event::Canceled { when, index });
+				Self::deposit_event(Event::Scheduled {
+					when: new_time,
+					index: new_index,
+				});
 
 				*lookup = Some((new_time, new_index));
 
@@ -684,161 +810,86 @@
 		)
 	}
 }
-
-#[cfg(test)]
-#[allow(clippy::from_over_into)]
-mod tests {
-	use super::*;
 
-	use frame_support::{
-		ord_parameter_types, parameter_types,
-		traits::{Contains, ConstU32, EnsureOneOf},
-		weights::constants::RocksDbWeight,
-	};
-	use sp_core::H256;
-	use sp_runtime::{
-		Perbill,
-		testing::Header,
-		traits::{BlakeTwo256, IdentityLookup},
-	};
-	use frame_system::{EnsureRoot, EnsureSignedBy};
-	use crate as scheduler;
+impl<T: Config> schedule::v2::Anon<T::BlockNumber, <T as Config>::Call, T::PalletsOrigin>
+	for Pallet<T>
+{
+	type Address = TaskAddress<T::BlockNumber>;
+	type Hash = T::Hash;
 
-	#[frame_support::pallet]
-	pub mod logger {
-		use super::{OriginCaller, OriginTrait};
-		use frame_support::pallet_prelude::*;
-		use frame_system::pallet_prelude::*;
-		use std::cell::RefCell;
-
-		thread_local! {
-			static LOG: RefCell<Vec<(OriginCaller, u32)>> = RefCell::new(Vec::new());
-		}
-		pub fn log() -> Vec<(OriginCaller, u32)> {
-			LOG.with(|log| log.borrow().clone())
-		}
-
-		#[pallet::pallet]
-		#[pallet::generate_store(pub(super) trait Store)]
-		pub struct Pallet<T>(PhantomData<T>);
-
-		#[pallet::hooks]
-		impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
-
-		#[pallet::config]
-		pub trait Config: frame_system::Config {
-			type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
-		}
-
-		#[pallet::event]
-		#[pallet::generate_deposit(pub(super) fn deposit_event)]
-		pub enum Event<T: Config> {
-			Logged(u32, Weight),
-		}
+	fn schedule(
+		when: DispatchTime<T::BlockNumber>,
+		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,
+		priority: schedule::Priority,
+		origin: T::PalletsOrigin,
+		call: CallOrHashOf<T>,
+	) -> Result<Self::Address, DispatchError> {
+		Self::do_schedule(when, maybe_periodic, priority, origin, call)
+	}
 
-		#[pallet::call]
-		impl<T: Config> Pallet<T>
-		where
-			<T as frame_system::Config>::Origin: OriginTrait<PalletsOrigin = OriginCaller>,
-		{
-			#[pallet::weight(*weight)]
-			pub fn log(origin: OriginFor<T>, i: u32, weight: Weight) -> DispatchResult {
-				Self::deposit_event(Event::Logged(i, weight));
-				LOG.with(|log| {
-					log.borrow_mut().push((origin.caller().clone(), i));
-				});
-				Ok(())
-			}
+	fn cancel((when, index): Self::Address) -> Result<(), ()> {
+		Self::do_cancel(None, (when, index)).map_err(|_| ())
+	}
 
-			#[pallet::weight(*weight)]
-			pub fn log_without_filter(
-				origin: OriginFor<T>,
-				i: u32,
-				weight: Weight,
-			) -> DispatchResult {
-				Self::deposit_event(Event::Logged(i, weight));
-				LOG.with(|log| {
-					log.borrow_mut().push((origin.caller().clone(), i));
-				});
-				Ok(())
-			}
-		}
+	fn reschedule(
+		address: Self::Address,
+		when: DispatchTime<T::BlockNumber>,
+	) -> Result<Self::Address, DispatchError> {
+		Self::do_reschedule(address, when)
 	}
 
-	type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
-	type Block = frame_system::mocking::MockBlock<Test>;
+	fn next_dispatch_time((when, index): Self::Address) -> Result<T::BlockNumber, ()> {
+		Agenda::<T>::get(when)
+			.get(index as usize)
+			.ok_or(())
+			.map(|_| when)
+	}
+}
 
-	frame_support::construct_runtime!(
-		pub enum Test where
-			Block = Block,
-			NodeBlock = Block,
-			UncheckedExtrinsic = UncheckedExtrinsic,
-		{
-			System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
-			Logger: logger::{Pallet, Call, Event<T>},
-			Scheduler: scheduler::{Pallet, Call, Storage, Event<T>},
-		}
-	);
+impl<T: Config> schedule::v2::Named<T::BlockNumber, <T as Config>::Call, T::PalletsOrigin>
+	for Pallet<T>
+{
+	type Address = TaskAddress<T::BlockNumber>;
+	type Hash = T::Hash;
 
-	// Scheduler must dispatch with root and no filter, this tests base filter is indeed not used.
-	pub struct BaseFilter;
-	impl Contains<Call> for BaseFilter {
-		fn contains(call: &Call) -> bool {
-			!matches!(call, Call::Logger(logger::Call::log { .. }))
-		}
+	fn schedule_named(
+		id: Vec<u8>,
+		when: DispatchTime<T::BlockNumber>,
+		maybe_periodic: Option<schedule::Period<T::BlockNumber>>,
+		priority: schedule::Priority,
+		origin: T::PalletsOrigin,
+		call: CallOrHashOf<T>,
+	) -> Result<Self::Address, ()> {
+		let inner_id: ScheduledId = id
+			.try_into()
+			.unwrap_or([0; MAX_TASK_ID_LENGTH_IN_BYTES as usize]);
+		Self::do_schedule_named(inner_id, when, maybe_periodic, priority, origin, call)
+			.map_err(|_| ())
 	}
 
-	parameter_types! {
-		pub const BlockHashCount: u64 = 250;
-		pub BlockWeights: frame_system::limits::BlockWeights =
-			frame_system::limits::BlockWeights::simple_max(2_000_000_000_000);
+	fn cancel_named(id: Vec<u8>) -> Result<(), ()> {
+		let inner_id: ScheduledId = id
+			.try_into()
+			.unwrap_or([0; MAX_TASK_ID_LENGTH_IN_BYTES as usize]);
+		Self::do_cancel_named(None, inner_id).map_err(|_| ())
 	}
-	impl system::Config for Test {
-		type BaseCallFilter = BaseFilter;
-		type BlockWeights = ();
-		type BlockLength = ();
-		type DbWeight = RocksDbWeight;
-		type Origin = Origin;
-		type Call = Call;
-		type Index = u64;
-		type BlockNumber = u64;
-		type Hash = H256;
-		type Hashing = BlakeTwo256;
-		type AccountId = u64;
-		type Lookup = IdentityLookup<Self::AccountId>;
-		type Header = Header;
-		type Event = Event;
-		type BlockHashCount = BlockHashCount;
-		type Version = ();
-		type PalletInfo = PalletInfo;
-		type AccountData = ();
-		type OnNewAccount = ();
-		type OnKilledAccount = ();
-		type SystemWeightInfo = ();
-		type SS58Prefix = ();
-		type OnSetCode = ();
-		type MaxConsumers = ConstU32<16>;
-	}
-	impl logger::Config for Test {
-		type Event = Event;
-	}
-	parameter_types! {
-		pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * BlockWeights::get().max_block;
-		pub const MaxScheduledPerBlock: u32 = 10;
-	}
-	ord_parameter_types! {
-		pub const One: u64 = 1;
+
+	fn reschedule_named(
+		id: Vec<u8>,
+		when: DispatchTime<T::BlockNumber>,
+	) -> Result<Self::Address, DispatchError> {
+		let inner_id: ScheduledId = id
+			.try_into()
+			.unwrap_or([0; MAX_TASK_ID_LENGTH_IN_BYTES as usize]);
+		Self::do_reschedule_named(inner_id, when)
 	}
 
-	impl Config for Test {
-		type Event = Event;
-		type Origin = Origin;
-		type PalletsOrigin = OriginCaller;
-		type Call = Call;
-		type MaximumWeight = MaximumSchedulerWeight;
-		type ScheduleOrigin = EnsureOneOf<EnsureRoot<u64>, EnsureSignedBy<One, u64>>;
-		type MaxScheduledPerBlock = MaxScheduledPerBlock;
-		type WeightInfo = ();
-		type SponsorshipHandler = ();
+	fn next_dispatch_time(id: Vec<u8>) -> Result<T::BlockNumber, ()> {
+		let inner_id: ScheduledId = id
+			.try_into()
+			.unwrap_or([0; MAX_TASK_ID_LENGTH_IN_BYTES as usize]);
+		Lookup::<T>::get(inner_id)
+			.and_then(|(when, index)| Agenda::<T>::get(when).get(index as usize).map(|_| when))
+			.ok_or(())
 	}
 }
modifiedpallets/scheduler/src/weights.rsdiffbeforeafterboth
--- a/pallets/scheduler/src/weights.rs
+++ b/pallets/scheduler/src/weights.rs
@@ -1,23 +1,6 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-// Original license
 // This file is part of Substrate.
 
-// Copyright (C) 2020-2021 Parity Technologies (UK) Ltd.
+// Copyright (C) 2022 Parity Technologies (UK) Ltd.
 // SPDX-License-Identifier: Apache-2.0
 
 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -32,13 +15,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-//! Weights for pallet_scheduler
-//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0
-//! DATE: 2020-10-27, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: [], HIGH RANGE: []
-//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128
+//! Autogenerated weights for pallet_scheduler
+//!
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
+//! DATE: 2022-01-31, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024
 
 // Executed Command:
-// target/release/substrate
+// ./target/production/substrate
 // benchmark
 // --chain=dev
 // --steps=50
@@ -49,78 +33,332 @@
 // --wasm-execution=compiled
 // --heap-pages=4096
 // --output=./frame/scheduler/src/weights.rs
-// --template=./.maintain/frame-weight-template.hbs
+// --template=.maintain/frame-weight-template.hbs
+// --header=HEADER-APACHE2
+// --raw
 
+#![cfg_attr(rustfmt, rustfmt_skip)]
 #![allow(unused_parens)]
 #![allow(unused_imports)]
 
-use frame_support::{
-	traits::Get,
-	weights::{Weight, constants::RocksDbWeight},
-};
+use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
 use sp_std::marker::PhantomData;
 
 /// Weight functions needed for pallet_scheduler.
 pub trait WeightInfo {
-	fn schedule(s: u32) -> Weight;
-	fn cancel(s: u32) -> Weight;
-	fn schedule_named(s: u32) -> Weight;
-	fn cancel_named(s: u32) -> Weight;
+	fn on_initialize_periodic_named_resolved(s: u32, ) -> Weight;
+	fn on_initialize_named_resolved(s: u32, ) -> Weight;
+	fn on_initialize_periodic_resolved(s: u32, ) -> Weight;
+	fn on_initialize_resolved(s: u32, ) -> Weight;
+	fn on_initialize_named_aborted(s: u32, ) -> Weight;
+	fn on_initialize_aborted(s: u32, ) -> Weight;
+	fn on_initialize_periodic_named(s: u32, ) -> Weight;
+	fn on_initialize_periodic(s: u32, ) -> Weight;
+	fn on_initialize_named(s: u32, ) -> Weight;
+	fn on_initialize(s: u32, ) -> Weight;
+	fn schedule(s: u32, ) -> Weight;
+	fn cancel(s: u32, ) -> Weight;
+	fn schedule_named(s: u32, ) -> Weight;
+	fn cancel_named(s: u32, ) -> Weight;
 }
 
 /// Weights for pallet_scheduler using the Substrate node and recommended hardware.
 pub struct SubstrateWeight<T>(PhantomData<T>);
 impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
-	fn schedule(s: u32) -> Weight {
-		35_029_000_u64
-			.saturating_add(77_000_u64.saturating_mul(s as Weight))
-			.saturating_add(T::DbWeight::get().reads(1_u64))
-			.saturating_add(T::DbWeight::get().writes(1_u64))
+	// Storage: Scheduler Agenda (r:2 w:2)
+	// Storage: Preimage PreimageFor (r:1 w:1)
+	// Storage: Preimage StatusFor (r:1 w:1)
+	// Storage: Scheduler Lookup (r:0 w:1)
+	fn on_initialize_periodic_named_resolved(s: u32, ) -> Weight {
+		(11_587_000 as Weight)
+			// Standard Error: 17_000
+			.saturating_add((17_428_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().reads((3 as Weight).saturating_mul(s as Weight)))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes((4 as Weight).saturating_mul(s as Weight)))
+	}
+	// Storage: Scheduler Agenda (r:1 w:1)
+	// Storage: Preimage PreimageFor (r:1 w:1)
+	// Storage: Preimage StatusFor (r:1 w:1)
+	// Storage: Scheduler Lookup (r:0 w:1)
+	fn on_initialize_named_resolved(s: u32, ) -> Weight {
+		(8_965_000 as Weight)
+			// Standard Error: 11_000
+			.saturating_add((13_410_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(s as Weight)))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes((3 as Weight).saturating_mul(s as Weight)))
 	}
-	fn cancel(s: u32) -> Weight {
-		31_419_000_u64
-			.saturating_add(4_015_000_u64.saturating_mul(s as Weight))
-			.saturating_add(T::DbWeight::get().reads(1_u64))
-			.saturating_add(T::DbWeight::get().writes(2_u64))
+	// Storage: Scheduler Agenda (r:2 w:2)
+	// Storage: Preimage PreimageFor (r:1 w:1)
+	// Storage: Preimage StatusFor (r:1 w:1)
+	fn on_initialize_periodic_resolved(s: u32, ) -> Weight {
+		(8_654_000 as Weight)
+			// Standard Error: 17_000
+			.saturating_add((14_990_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().reads((3 as Weight).saturating_mul(s as Weight)))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes((3 as Weight).saturating_mul(s as Weight)))
+	}
+	// Storage: Scheduler Agenda (r:1 w:1)
+	// Storage: Preimage PreimageFor (r:1 w:1)
+	// Storage: Preimage StatusFor (r:1 w:1)
+	fn on_initialize_resolved(s: u32, ) -> Weight {
+		(9_303_000 as Weight)
+			// Standard Error: 10_000
+			.saturating_add((12_244_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(s as Weight)))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes((2 as Weight).saturating_mul(s as Weight)))
+	}
+	// Storage: Scheduler Agenda (r:2 w:2)
+	// Storage: Preimage PreimageFor (r:1 w:0)
+	// Storage: Scheduler Lookup (r:0 w:1)
+	fn on_initialize_named_aborted(s: u32, ) -> Weight {
+		(7_506_000 as Weight)
+			// Standard Error: 3_000
+			.saturating_add((5_208_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(T::DbWeight::get().reads(2 as Weight))
+			.saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(s as Weight)))
+			.saturating_add(T::DbWeight::get().writes(2 as Weight))
+			.saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(s as Weight)))
+	}
+	// Storage: Scheduler Agenda (r:2 w:2)
+	// Storage: Preimage PreimageFor (r:1 w:0)
+	fn on_initialize_aborted(s: u32, ) -> Weight {
+		(8_046_000 as Weight)
+			// Standard Error: 3_000
+			.saturating_add((2_914_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(T::DbWeight::get().reads(2 as Weight))
+			.saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(s as Weight)))
+			.saturating_add(T::DbWeight::get().writes(2 as Weight))
+	}
+	// Storage: Scheduler Agenda (r:2 w:2)
+	// Storage: Scheduler Lookup (r:0 w:1)
+	fn on_initialize_periodic_named(s: u32, ) -> Weight {
+		(13_704_000 as Weight)
+			// Standard Error: 4_000
+			.saturating_add((8_186_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(s as Weight)))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes((2 as Weight).saturating_mul(s as Weight)))
+	}
+	// Storage: Scheduler Agenda (r:2 w:2)
+	fn on_initialize_periodic(s: u32, ) -> Weight {
+		(12_668_000 as Weight)
+			// Standard Error: 5_000
+			.saturating_add((5_868_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(s as Weight)))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(s as Weight)))
+	}
+	// Storage: Scheduler Agenda (r:1 w:1)
+	// Storage: Scheduler Lookup (r:0 w:1)
+	fn on_initialize_named(s: u32, ) -> Weight {
+		(13_946_000 as Weight)
+			// Standard Error: 4_000
+			.saturating_add((4_367_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(s as Weight)))
+	}
+	// Storage: Scheduler Agenda (r:1 w:1)
+	fn on_initialize(s: u32, ) -> Weight {
+		(13_151_000 as Weight)
+			// Standard Error: 4_000
+			.saturating_add((3_455_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	// Storage: Scheduler Agenda (r:1 w:1)
+	fn schedule(s: u32, ) -> Weight {
+		(14_040_000 as Weight)
+			// Standard Error: 1_000
+			.saturating_add((89_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	// Storage: Scheduler Agenda (r:1 w:1)
+	// Storage: Scheduler Lookup (r:0 w:1)
+	fn cancel(s: u32, ) -> Weight {
+		(14_376_000 as Weight)
+			// Standard Error: 1_000
+			.saturating_add((576_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(2 as Weight))
 	}
-	fn schedule_named(s: u32) -> Weight {
-		44_752_000_u64
-			.saturating_add(123_000_u64.saturating_mul(s as Weight))
-			.saturating_add(T::DbWeight::get().reads(2_u64))
-			.saturating_add(T::DbWeight::get().writes(2_u64))
+	// Storage: Scheduler Lookup (r:1 w:1)
+	// Storage: Scheduler Agenda (r:1 w:1)
+	fn schedule_named(s: u32, ) -> Weight {
+		(16_806_000 as Weight)
+			// Standard Error: 1_000
+			.saturating_add((102_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(T::DbWeight::get().reads(2 as Weight))
+			.saturating_add(T::DbWeight::get().writes(2 as Weight))
 	}
-	fn cancel_named(s: u32) -> Weight {
-		35_712_000_u64
-			.saturating_add(4_008_000_u64.saturating_mul(s as Weight))
-			.saturating_add(T::DbWeight::get().reads(2_u64))
-			.saturating_add(T::DbWeight::get().writes(2_u64))
+	// Storage: Scheduler Lookup (r:1 w:1)
+	// Storage: Scheduler Agenda (r:1 w:1)
+	fn cancel_named(s: u32, ) -> Weight {
+		(15_852_000 as Weight)
+			// Standard Error: 2_000
+			.saturating_add((590_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(T::DbWeight::get().reads(2 as Weight))
+			.saturating_add(T::DbWeight::get().writes(2 as Weight))
 	}
 }
 
 // For backwards compatibility and tests
 impl WeightInfo for () {
-	fn schedule(s: u32) -> Weight {
-		35_029_000_u64
-			.saturating_add(77_000_u64.saturating_mul(s as Weight))
-			.saturating_add(RocksDbWeight::get().reads(1_u64))
-			.saturating_add(RocksDbWeight::get().writes(1_u64))
+	// Storage: Scheduler Agenda (r:2 w:2)
+	// Storage: Preimage PreimageFor (r:1 w:1)
+	// Storage: Preimage StatusFor (r:1 w:1)
+	// Storage: Scheduler Lookup (r:0 w:1)
+	fn on_initialize_periodic_named_resolved(s: u32, ) -> Weight {
+		(11_587_000 as Weight)
+			// Standard Error: 17_000
+			.saturating_add((17_428_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().reads((3 as Weight).saturating_mul(s as Weight)))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes((4 as Weight).saturating_mul(s as Weight)))
+	}
+	// Storage: Scheduler Agenda (r:1 w:1)
+	// Storage: Preimage PreimageFor (r:1 w:1)
+	// Storage: Preimage StatusFor (r:1 w:1)
+	// Storage: Scheduler Lookup (r:0 w:1)
+	fn on_initialize_named_resolved(s: u32, ) -> Weight {
+		(8_965_000 as Weight)
+			// Standard Error: 11_000
+			.saturating_add((13_410_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().reads((2 as Weight).saturating_mul(s as Weight)))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes((3 as Weight).saturating_mul(s as Weight)))
 	}
-	fn cancel(s: u32) -> Weight {
-		31_419_000_u64
-			.saturating_add(4_015_000_u64.saturating_mul(s as Weight))
-			.saturating_add(RocksDbWeight::get().reads(1_u64))
-			.saturating_add(RocksDbWeight::get().writes(2_u64))
+	// Storage: Scheduler Agenda (r:2 w:2)
+	// Storage: Preimage PreimageFor (r:1 w:1)
+	// Storage: Preimage StatusFor (r:1 w:1)
+	fn on_initialize_periodic_resolved(s: u32, ) -> Weight {
+		(8_654_000 as Weight)
+			// Standard Error: 17_000
+			.saturating_add((14_990_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().reads((3 as Weight).saturating_mul(s as Weight)))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes((3 as Weight).saturating_mul(s as Weight)))
 	}
-	fn schedule_named(s: u32) -> Weight {
-		44_752_000_u64
-			.saturating_add(123_000_u64.saturating_mul(s as Weight))
-			.saturating_add(RocksDbWeight::get().reads(2_u64))
-			.saturating_add(RocksDbWeight::get().writes(2_u64))
+	// Storage: Scheduler Agenda (r:1 w:1)
+	// Storage: Preimage PreimageFor (r:1 w:1)
+	// Storage: Preimage StatusFor (r:1 w:1)
+	fn on_initialize_resolved(s: u32, ) -> Weight {
+		(9_303_000 as Weight)
+			// Standard Error: 10_000
+			.saturating_add((12_244_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().reads((2 as Weight).saturating_mul(s as Weight)))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes((2 as Weight).saturating_mul(s as Weight)))
 	}
-	fn cancel_named(s: u32) -> Weight {
-		35_712_000_u64
-			.saturating_add(4_008_000_u64.saturating_mul(s as Weight))
-			.saturating_add(RocksDbWeight::get().reads(2_u64))
-			.saturating_add(RocksDbWeight::get().writes(2_u64))
+	// Storage: Scheduler Agenda (r:2 w:2)
+	// Storage: Preimage PreimageFor (r:1 w:0)
+	// Storage: Scheduler Lookup (r:0 w:1)
+	fn on_initialize_named_aborted(s: u32, ) -> Weight {
+		(7_506_000 as Weight)
+			// Standard Error: 3_000
+			.saturating_add((5_208_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(RocksDbWeight::get().reads(2 as Weight))
+			.saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(s as Weight)))
+			.saturating_add(RocksDbWeight::get().writes(2 as Weight))
+			.saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(s as Weight)))
+	}
+	// Storage: Scheduler Agenda (r:2 w:2)
+	// Storage: Preimage PreimageFor (r:1 w:0)
+	fn on_initialize_aborted(s: u32, ) -> Weight {
+		(8_046_000 as Weight)
+			// Standard Error: 3_000
+			.saturating_add((2_914_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(RocksDbWeight::get().reads(2 as Weight))
+			.saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(s as Weight)))
+			.saturating_add(RocksDbWeight::get().writes(2 as Weight))
+	}
+	// Storage: Scheduler Agenda (r:2 w:2)
+	// Storage: Scheduler Lookup (r:0 w:1)
+	fn on_initialize_periodic_named(s: u32, ) -> Weight {
+		(13_704_000 as Weight)
+			// Standard Error: 4_000
+			.saturating_add((8_186_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(s as Weight)))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes((2 as Weight).saturating_mul(s as Weight)))
+	}
+	// Storage: Scheduler Agenda (r:2 w:2)
+	fn on_initialize_periodic(s: u32, ) -> Weight {
+		(12_668_000 as Weight)
+			// Standard Error: 5_000
+			.saturating_add((5_868_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(s as Weight)))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(s as Weight)))
+	}
+	// Storage: Scheduler Agenda (r:1 w:1)
+	// Storage: Scheduler Lookup (r:0 w:1)
+	fn on_initialize_named(s: u32, ) -> Weight {
+		(13_946_000 as Weight)
+			// Standard Error: 4_000
+			.saturating_add((4_367_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(s as Weight)))
+	}
+	// Storage: Scheduler Agenda (r:1 w:1)
+	fn on_initialize(s: u32, ) -> Weight {
+		(13_151_000 as Weight)
+			// Standard Error: 4_000
+			.saturating_add((3_455_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	// Storage: Scheduler Agenda (r:1 w:1)
+	fn schedule(s: u32, ) -> Weight {
+		(14_040_000 as Weight)
+			// Standard Error: 1_000
+			.saturating_add((89_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	// Storage: Scheduler Agenda (r:1 w:1)
+	// Storage: Scheduler Lookup (r:0 w:1)
+	fn cancel(s: u32, ) -> Weight {
+		(14_376_000 as Weight)
+			// Standard Error: 1_000
+			.saturating_add((576_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(2 as Weight))
+	}
+	// Storage: Scheduler Lookup (r:1 w:1)
+	// Storage: Scheduler Agenda (r:1 w:1)
+	fn schedule_named(s: u32, ) -> Weight {
+		(16_806_000 as Weight)
+			// Standard Error: 1_000
+			.saturating_add((102_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(RocksDbWeight::get().reads(2 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(2 as Weight))
+	}
+	// Storage: Scheduler Lookup (r:1 w:1)
+	// Storage: Scheduler Agenda (r:1 w:1)
+	fn cancel_named(s: u32, ) -> Weight {
+		(15_852_000 as Weight)
+			// Standard Error: 2_000
+			.saturating_add((590_000 as Weight).saturating_mul(s as Weight))
+			.saturating_add(RocksDbWeight::get().reads(2 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(2 as Weight))
 	}
 }
modifiedruntime/opal/src/lib.rsdiffbeforeafterboth
28use sp_api::impl_runtime_apis;28use sp_api::impl_runtime_apis;
29use sp_core::{crypto::KeyTypeId, OpaqueMetadata, H256, U256, H160};29use sp_core::{crypto::KeyTypeId, OpaqueMetadata, H256, U256, H160};
30use sp_runtime::DispatchError;30use sp_runtime::DispatchError;
31use fp_self_contained::*;
32use sp_runtime::traits::{Member};
31// #[cfg(any(feature = "std", test))]33// #[cfg(any(feature = "std", test))]
32// pub use sp_runtime::BuildStorage;34// pub use sp_runtime::BuildStorage;
3335
59 traits::{61 traits::{
60 tokens::currency::Currency as CurrencyT, OnUnbalanced as OnUnbalancedT, Everything,62 tokens::currency::Currency as CurrencyT, OnUnbalanced as OnUnbalancedT, Everything,
61 Currency, ExistenceRequirement, Get, IsInVec, KeyOwnerProofSystem, LockIdentifier,63 Currency, ExistenceRequirement, Get, IsInVec, KeyOwnerProofSystem, LockIdentifier,
62 OnUnbalanced, Randomness, FindAuthor, ConstU32, Imbalance,64 OnUnbalanced, Randomness, FindAuthor, ConstU32, Imbalance, PrivilegeCmp,
63 },65 },
64 weights::{66 weights::{
65 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},67 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},
66 DispatchClass, DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,68 DispatchClass, DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,
67 WeightToFeePolynomial, WeightToFeeCoefficient, WeightToFeeCoefficients, ConstantMultiplier,69 WeightToFeePolynomial, WeightToFeeCoefficient, WeightToFeeCoefficients, ConstantMultiplier,
68 },70 },
69};71};
72use pallet_unq_scheduler::DispatchCall;
70use up_data_structs::mapping::{EvmTokenAddressMapping, CrossTokenAddressMapping};73use up_data_structs::{
71use up_data_structs::*;74 CollectionId, TokenId, TokenData, Property, PropertyKeyPermission, CollectionLimits,
75 CollectionStats, RpcCollection,
76 mapping::{EvmTokenAddressMapping, CrossTokenAddressMapping},
77};
78
72// use pallet_contracts::weights::WeightInfo;79// use pallet_contracts::weights::WeightInfo;
73// #[cfg(any(feature = "std", test))]80// #[cfg(any(feature = "std", test))]
79 traits::{BaseArithmetic, Unsigned},86 traits::{BaseArithmetic, Unsigned},
80};87};
81use smallvec::smallvec;88use smallvec::smallvec;
89// use scale_info::TypeInfo;
82use codec::{Encode, Decode};90use codec::{Encode, Decode};
83use fp_rpc::TransactionStatus;91use fp_rpc::TransactionStatus;
84use sp_runtime::{92use sp_runtime::{
85 traits::{BlockNumberProvider, Dispatchable, PostDispatchInfoOf, DispatchInfoOf, Saturating},93 traits::{
94 Applyable, BlockNumberProvider, Dispatchable, PostDispatchInfoOf, DispatchInfoOf,
95 Saturating, CheckedConversion,
96 },
97 generic::Era,
86 transaction_validity::TransactionValidityError,98 transaction_validity::TransactionValidityError,
87 SaturatedConversion,99 DispatchErrorWithPostInfo, SaturatedConversion,
88};100};
89101
90// pub use pallet_timestamp::Call as TimestampCall;102// pub use pallet_timestamp::Call as TimestampCall;
102 ParentIsPreset,114 ParentIsPreset,
103};115};
104use xcm_executor::{Config, XcmExecutor, Assets};116use xcm_executor::{Config, XcmExecutor, Assets};
105use sp_std::{marker::PhantomData};117use sp_std::{cmp::Ordering, marker::PhantomData};
106118
107use xcm::latest::{119use xcm::latest::{
108 // Xcm,120 // Xcm,
113};125};
114use xcm_executor::traits::{MatchesFungible, WeightTrader};126use xcm_executor::traits::{MatchesFungible, WeightTrader};
115//use xcm_executor::traits::MatchesFungible;127//use xcm_executor::traits::MatchesFungible;
116use sp_runtime::traits::CheckedConversion;
117128
118use unique_runtime_common::{129use unique_runtime_common::{
119 impl_common_runtime_apis,130 impl_common_runtime_apis,
406 // pub const ExistentialDeposit: u128 = 500;417 // pub const ExistentialDeposit: u128 = 500;
407 pub const ExistentialDeposit: u128 = 0;418 pub const ExistentialDeposit: u128 = 0;
408 pub const MaxLocks: u32 = 50;419 pub const MaxLocks: u32 = 50;
420 pub const MaxReserves: u32 = 50;
409}421}
410422
411impl pallet_balances::Config for Runtime {423impl pallet_balances::Config for Runtime {
412 type MaxLocks = MaxLocks;424 type MaxLocks = MaxLocks;
413 type MaxReserves = ();425 type MaxReserves = MaxReserves;
414 type ReserveIdentifier = [u8; 8];426 type ReserveIdentifier = [u8; 16];
415 /// The type for recording an account's balance.427 /// The type for recording an account's balance.
416 type Balance = Balance;428 type Balance = Balance;
417 /// The ubiquitous event type.429 /// The ubiquitous event type.
930 type BlockNumberProvider = RelayChainBlockNumberProvider<Runtime>;942 type BlockNumberProvider = RelayChainBlockNumberProvider<Runtime>;
931}943}
932944
933// parameter_types! {945parameter_types! {
934// pub MaximumSchedulerWeight: Weight = Perbill::from_percent(50) *946 pub MaximumSchedulerWeight: Weight = Perbill::from_percent(50) *
935// RuntimeBlockWeights::get().max_block;947 RuntimeBlockWeights::get().max_block;
936// pub const MaxScheduledPerBlock: u32 = 50;948 pub const MaxScheduledPerBlock: u32 = 50;
937// }949}
950
951type ChargeTransactionPayment = pallet_charge_transaction::ChargeTransactionPayment<Runtime>;
952use frame_support::traits::NamedReservableCurrency;
953
954fn get_signed_extras(from: <Runtime as frame_system::Config>::AccountId) -> SignedExtraScheduler {
955 (
956 frame_system::CheckSpecVersion::<Runtime>::new(),
957 frame_system::CheckGenesis::<Runtime>::new(),
958 frame_system::CheckEra::<Runtime>::from(Era::Immortal),
959 frame_system::CheckNonce::<Runtime>::from(frame_system::Pallet::<Runtime>::account_nonce(
960 from,
961 )),
962 frame_system::CheckWeight::<Runtime>::new(),
963 // sponsoring transaction logic
964 // pallet_charge_transaction::ChargeTransactionPayment::<Runtime>::new(0),
965 )
966}
967
968pub struct SchedulerPaymentExecutor;
969impl<T: frame_system::Config + pallet_unq_scheduler::Config, SelfContainedSignedInfo>
970 DispatchCall<T, SelfContainedSignedInfo> for SchedulerPaymentExecutor
971where
972 <T as frame_system::Config>::Call: Member
973 + Dispatchable<Origin = Origin, Info = DispatchInfo>
974 + SelfContainedCall<SignedInfo = SelfContainedSignedInfo>
975 + GetDispatchInfo
976 + From<frame_system::Call<Runtime>>,
977 SelfContainedSignedInfo: Send + Sync + 'static,
978 Call: From<<T as frame_system::Config>::Call>
979 + From<<T as pallet_unq_scheduler::Config>::Call>
980 + SelfContainedCall<SignedInfo = SelfContainedSignedInfo>,
981 sp_runtime::AccountId32: From<<T as frame_system::Config>::AccountId>,
982{
983 fn dispatch_call(
984 signer: <T as frame_system::Config>::AccountId,
985 call: <T as pallet_unq_scheduler::Config>::Call,
986 ) -> Result<
987 Result<PostDispatchInfo, DispatchErrorWithPostInfo<PostDispatchInfo>>,
988 TransactionValidityError,
989 > {
990 let dispatch_info = call.get_dispatch_info();
991 let extrinsic = fp_self_contained::CheckedExtrinsic::<
992 AccountId,
993 Call,
994 SignedExtraScheduler,
995 SelfContainedSignedInfo,
996 > {
997 signed:
998 CheckedSignature::<AccountId, SignedExtraScheduler, SelfContainedSignedInfo>::Signed(
999 signer.clone().into(),
1000 get_signed_extras(signer.into()),
1001 ),
1002 function: call.into(),
1003 };
1004
1005 extrinsic.apply::<Runtime>(&dispatch_info, 0)
1006 }
1007
1008 fn reserve_balance(
1009 id: [u8; 16],
1010 sponsor: <T as frame_system::Config>::AccountId,
1011 call: <T as pallet_unq_scheduler::Config>::Call,
1012 count: u32,
1013 ) -> Result<(), DispatchError> {
1014 let dispatch_info = call.get_dispatch_info();
1015 let weight: Balance = ChargeTransactionPayment::traditional_fee(0, &dispatch_info, 0)
1016 .saturating_mul(count.into());
1017
1018 <Balances as NamedReservableCurrency<AccountId>>::reserve_named(
1019 &id,
1020 &(sponsor.into()),
1021 weight,
1022 )
1023 }
1024
1025 fn pay_for_call(
1026 id: [u8; 16],
1027 sponsor: <T as frame_system::Config>::AccountId,
1028 call: <T as pallet_unq_scheduler::Config>::Call,
1029 ) -> Result<u128, DispatchError> {
1030 let dispatch_info = call.get_dispatch_info();
1031 let weight: Balance = ChargeTransactionPayment::traditional_fee(0, &dispatch_info, 0);
1032 Ok(
1033 <Balances as NamedReservableCurrency<AccountId>>::unreserve_named(
1034 &id,
1035 &(sponsor.into()),
1036 weight,
1037 ),
1038 )
1039 }
1040
1041 fn cancel_reserve(
1042 id: [u8; 16],
1043 sponsor: <T as frame_system::Config>::AccountId,
1044 ) -> Result<u128, DispatchError> {
1045 Ok(
1046 <Balances as NamedReservableCurrency<AccountId>>::unreserve_named(
1047 &id,
1048 &(sponsor.into()),
1049 u128::MAX,
1050 ),
1051 )
1052 }
1053}
1054
1055parameter_types! {
1056 pub const NoPreimagePostponement: Option<u32> = Some(10);
1057 pub const Preimage: Option<u32> = Some(10);
1058}
1059
1060/// Used the compare the privilege of an origin inside the scheduler.
1061pub struct OriginPrivilegeCmp;
1062
1063impl PrivilegeCmp<OriginCaller> for OriginPrivilegeCmp {
1064 fn cmp_privilege(_left: &OriginCaller, _right: &OriginCaller) -> Option<Ordering> {
1065 Some(Ordering::Equal)
1066 }
1067}
1068
1069impl pallet_unq_scheduler::Config for Runtime {
1070 type Event = Event;
1071 type Origin = Origin;
1072 type Currency = Balances;
1073 type PalletsOrigin = OriginCaller;
1074 type Call = Call;
1075 type MaximumWeight = MaximumSchedulerWeight;
1076 type ScheduleOrigin = EnsureSigned<AccountId>;
1077 type MaxScheduledPerBlock = MaxScheduledPerBlock;
1078 type WeightInfo = ();
1079 type CallExecutor = SchedulerPaymentExecutor;
1080 type OriginPrivilegeCmp = OriginPrivilegeCmp;
1081 type PreimageProvider = ();
1082 type NoPreimagePostponement = NoPreimagePostponement;
1083}
9381084
939type EvmSponsorshipHandler = (1085type EvmSponsorshipHandler = (
940 UniqueEthSponsorshipHandler<Runtime>,1086 UniqueEthSponsorshipHandler<Runtime>,
946 pallet_evm_transaction_payment::BridgeSponsorshipHandler<Runtime>,1093 pallet_evm_transaction_payment::BridgeSponsorshipHandler<Runtime>,
947);1094);
948
949// impl pallet_unq_scheduler::Config for Runtime {
950// type Event = Event;
951// type Origin = Origin;
952// type PalletsOrigin = OriginCaller;
953// type Call = Call;
954// type MaximumWeight = MaximumSchedulerWeight;
955// type ScheduleOrigin = EnsureSigned<AccountId>;
956// type MaxScheduledPerBlock = MaxScheduledPerBlock;
957// type SponsorshipHandler = SponsorshipHandler;
958// type WeightInfo = ();
959// }
9601095
961impl pallet_evm_transaction_payment::Config for Runtime {1096impl pallet_evm_transaction_payment::Config for Runtime {
962 type EvmSponsorshipHandler = EvmSponsorshipHandler;1097 type EvmSponsorshipHandler = EvmSponsorshipHandler;
1020 // Unique Pallets1155 // Unique Pallets
1021 Inflation: pallet_inflation::{Pallet, Call, Storage} = 60,1156 Inflation: pallet_inflation::{Pallet, Call, Storage} = 60,
1022 Unique: pallet_unique::{Pallet, Call, Storage, Event<T>} = 61,1157 Unique: pallet_unique::{Pallet, Call, Storage, Event<T>} = 61,
1023 // Scheduler: pallet_unq_scheduler::{Pallet, Call, Storage, Event<T>} = 62,1158 Scheduler: pallet_unq_scheduler::{Pallet, Call, Storage, Event<T>} = 62,
1024 // free = 631159 // free = 63
1025 Charging: pallet_charge_transaction::{Pallet, Call, Storage } = 64,1160 Charging: pallet_charge_transaction::{Pallet, Call, Storage } = 64,
1026 // ContractHelpers: pallet_contract_helpers::{Pallet, Call, Storage} = 65,1161 // ContractHelpers: pallet_contract_helpers::{Pallet, Call, Storage} = 65,
1087 frame_system::CheckEra<Runtime>,1222 frame_system::CheckEra<Runtime>,
1088 frame_system::CheckNonce<Runtime>,1223 frame_system::CheckNonce<Runtime>,
1089 frame_system::CheckWeight<Runtime>,1224 frame_system::CheckWeight<Runtime>,
1090 pallet_charge_transaction::ChargeTransactionPayment<Runtime>,1225 ChargeTransactionPayment,
1091 //pallet_contract_helpers::ContractHelpersExtension<Runtime>,1226 //pallet_contract_helpers::ContractHelpersExtension<Runtime>,
1092 pallet_ethereum::FakeTransactionFinalizer<Runtime>,1227 pallet_ethereum::FakeTransactionFinalizer<Runtime>,
1093);1228);
1229pub type SignedExtraScheduler = (
1230 frame_system::CheckSpecVersion<Runtime>,
1231 frame_system::CheckGenesis<Runtime>,
1232 frame_system::CheckEra<Runtime>,
1233 frame_system::CheckNonce<Runtime>,
1234 frame_system::CheckWeight<Runtime>,
1235);
1094/// Unchecked extrinsic type as expected by this runtime.1236/// Unchecked extrinsic type as expected by this runtime.
1095pub type UncheckedExtrinsic =1237pub type UncheckedExtrinsic =
1096 fp_self_contained::UncheckedExtrinsic<Address, Call, Signature, SignedExtra>;1238 fp_self_contained::UncheckedExtrinsic<Address, Call, Signature, SignedExtra>;
modifiedruntime/quartz/src/lib.rsdiffbeforeafterboth
--- a/runtime/quartz/src/lib.rs
+++ b/runtime/quartz/src/lib.rs
@@ -33,7 +33,7 @@
 
 use sp_runtime::{
 	Permill, Perbill, Percent, create_runtime_str, generic, impl_opaque_keys,
-	traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, AccountIdConversion, Zero},
+	traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, AccountIdConversion, Zero, Member},
 	transaction_validity::{TransactionSource, TransactionValidity},
 	ApplyExtrinsicResult, RuntimeAppPublic,
 };
@@ -58,7 +58,7 @@
 	traits::{
 		tokens::currency::Currency as CurrencyT, OnUnbalanced as OnUnbalancedT, Everything,
 		Currency, ExistenceRequirement, Get, IsInVec, KeyOwnerProofSystem, LockIdentifier,
-		OnUnbalanced, Randomness, FindAuthor, ConstU32, Imbalance,
+		OnUnbalanced, Randomness, FindAuthor, ConstU32, Imbalance, PrivilegeCmp,
 	},
 	weights::{
 		constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},
@@ -81,18 +81,28 @@
 use pallet_evm::{Account as EVMAccount, FeeCalculator, GasWeightMapping};
 use fp_rpc::TransactionStatus;
 use sp_runtime::{
-	traits::{BlockNumberProvider, Dispatchable, PostDispatchInfoOf, Saturating},
+	traits::{
+		Applyable, BlockNumberProvider, Dispatchable, PostDispatchInfoOf, Saturating,
+		CheckedConversion,
+	},
+	generic::Era,
 	transaction_validity::TransactionValidityError,
-	SaturatedConversion,
+	SaturatedConversion, DispatchErrorWithPostInfo,
 };
 
+use fp_self_contained::{SelfContainedCall, CheckedSignature};
+
 // pub use pallet_timestamp::Call as TimestampCall;
 pub use sp_consensus_aura::sr25519::AuthorityId as AuraId;
 
 // Polkadot imports
 use pallet_xcm::XcmPassthrough;
 use polkadot_parachain::primitives::Sibling;
-use up_data_structs::mapping::{CrossTokenAddressMapping, EvmTokenAddressMapping};
+use up_data_structs::{
+	CollectionId, TokenId, TokenData, Property, PropertyKeyPermission, CollectionLimits, 
+	CollectionStats, RpcCollection, 
+	mapping::{EvmTokenAddressMapping, CrossTokenAddressMapping}
+};
 use xcm::v1::{BodyId, Junction::*, MultiLocation, NetworkId, Junctions::*};
 use xcm_builder::{
 	AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter,
@@ -102,7 +112,8 @@
 	ParentIsPreset,
 };
 use xcm_executor::{Config, XcmExecutor, Assets};
-use sp_std::{marker::PhantomData};
+use sp_std::{cmp::Ordering, marker::PhantomData};
+use pallet_unq_scheduler::DispatchCall;
 
 use xcm::latest::{
 	//	Xcm,
@@ -112,8 +123,6 @@
 	Error as XcmError,
 };
 use xcm_executor::traits::{MatchesFungible, WeightTrader};
-//use xcm_executor::traits::MatchesFungible;
-use sp_runtime::traits::CheckedConversion;
 
 use unique_runtime_common::{
 	impl_common_runtime_apis,
@@ -390,7 +399,7 @@
 impl pallet_balances::Config for Runtime {
 	type MaxLocks = MaxLocks;
 	type MaxReserves = ();
-	type ReserveIdentifier = [u8; 8];
+	type ReserveIdentifier = [u8; 16];
 	/// The type for recording an account's balance.
 	type Balance = Balance;
 	/// The ubiquitous event type.
@@ -913,11 +922,11 @@
 	type BlockNumberProvider = RelayChainBlockNumberProvider<Runtime>;
 }
 
-// parameter_types! {
-// 	pub MaximumSchedulerWeight: Weight = Perbill::from_percent(50) *
-// 		RuntimeBlockWeights::get().max_block;
-// 	pub const MaxScheduledPerBlock: u32 = 50;
-// }
+parameter_types! {
+	pub MaximumSchedulerWeight: Weight = Perbill::from_percent(50) *
+		RuntimeBlockWeights::get().max_block;
+	pub const MaxScheduledPerBlock: u32 = 50;
+}
 
 type EvmSponsorshipHandler = (
 	UniqueEthSponsorshipHandler<Runtime>,
@@ -929,17 +938,34 @@
 	pallet_evm_transaction_payment::BridgeSponsorshipHandler<Runtime>,
 );
 
-// impl pallet_unq_scheduler::Config for Runtime {
-// 	type Event = Event;
-// 	type Origin = Origin;
-// 	type PalletsOrigin = OriginCaller;
-// 	type Call = Call;
-// 	type MaximumWeight = MaximumSchedulerWeight;
-// 	type ScheduleOrigin = EnsureSigned<AccountId>;
-// 	type MaxScheduledPerBlock = MaxScheduledPerBlock;
-// 	type SponsorshipHandler = SponsorshipHandler;
-// 	type WeightInfo = ();
-// }
+parameter_types! {
+	pub const NoPreimagePostponement: Option<u32> = Some(10);
+	pub const Preimage: Option<u32> = Some(10);
+}
+
+/// Used the compare the privilege of an origin inside the scheduler.
+pub struct OriginPrivilegeCmp;
+impl PrivilegeCmp<OriginCaller> for OriginPrivilegeCmp {
+	fn cmp_privilege(_left: &OriginCaller, _right: &OriginCaller) -> Option<Ordering> {
+		Some(Ordering::Equal)
+	}
+}
+
+impl pallet_unq_scheduler::Config for Runtime {
+	type Event = Event;
+	type Origin = Origin;
+	type Currency = Balances;
+	type PalletsOrigin = OriginCaller;
+	type Call = Call;
+	type MaximumWeight = MaximumSchedulerWeight;
+	type ScheduleOrigin = EnsureSigned<AccountId>;
+	type MaxScheduledPerBlock = MaxScheduledPerBlock;
+	type WeightInfo = ();
+	type CallExecutor = SchedulerPaymentExecutor;
+	type OriginPrivilegeCmp = OriginPrivilegeCmp;
+	type PreimageProvider = ();
+	type NoPreimagePostponement = NoPreimagePostponement;
+}
 
 impl pallet_evm_transaction_payment::Config for Runtime {
 	type EvmSponsorshipHandler = EvmSponsorshipHandler;
@@ -954,6 +980,110 @@
 //	 type DefaultSponsoringRateLimit = DefaultSponsoringRateLimit;
 // }
 
+type ChargeTransactionPayment = pallet_charge_transaction::ChargeTransactionPayment<Runtime>;
+use frame_support::traits::NamedReservableCurrency;
+
+fn get_signed_extras(from: <Runtime as frame_system::Config>::AccountId) -> SignedExtraScheduler {
+	(
+		frame_system::CheckSpecVersion::<Runtime>::new(),
+		frame_system::CheckGenesis::<Runtime>::new(),
+		frame_system::CheckEra::<Runtime>::from(Era::Immortal),
+		frame_system::CheckNonce::<Runtime>::from(frame_system::Pallet::<Runtime>::account_nonce(
+			from,
+		)),
+		frame_system::CheckWeight::<Runtime>::new(),
+		// sponsoring transaction logic
+		// pallet_charge_transaction::ChargeTransactionPayment::<Runtime>::new(0),
+	)
+}
+
+pub struct SchedulerPaymentExecutor;
+impl<T: frame_system::Config + pallet_unq_scheduler::Config, SelfContainedSignedInfo>
+	DispatchCall<T, SelfContainedSignedInfo> for SchedulerPaymentExecutor
+where
+	<T as frame_system::Config>::Call: Member
+		+ Dispatchable<Origin = Origin, Info = DispatchInfo>
+		+ SelfContainedCall<SignedInfo = SelfContainedSignedInfo>
+		+ GetDispatchInfo
+		+ From<frame_system::Call<Runtime>>,
+	SelfContainedSignedInfo: Send + Sync + 'static,
+	Call: From<<T as frame_system::Config>::Call>
+		+ From<<T as pallet_unq_scheduler::Config>::Call>
+		+ SelfContainedCall<SignedInfo = SelfContainedSignedInfo>,
+	sp_runtime::AccountId32: From<<T as frame_system::Config>::AccountId>,
+{
+	fn dispatch_call(
+		signer: <T as frame_system::Config>::AccountId,
+		call: <T as pallet_unq_scheduler::Config>::Call,
+	) -> Result<
+		Result<PostDispatchInfo, DispatchErrorWithPostInfo<PostDispatchInfo>>,
+		TransactionValidityError,
+	> {
+		let dispatch_info = call.get_dispatch_info();
+		let extrinsic = fp_self_contained::CheckedExtrinsic::<
+			AccountId,
+			Call,
+			SignedExtraScheduler,
+			SelfContainedSignedInfo,
+		> {
+			signed:
+				CheckedSignature::<AccountId, SignedExtraScheduler, SelfContainedSignedInfo>::Signed(
+					signer.clone().into(),
+					get_signed_extras(signer.into()),
+				),
+			function: call.into(),
+		};
+
+		extrinsic.apply::<Runtime>(&dispatch_info, 0)
+	}
+
+	fn reserve_balance(
+		id: [u8; 16],
+		sponsor: <T as frame_system::Config>::AccountId,
+		call: <T as pallet_unq_scheduler::Config>::Call,
+		count: u32,
+	) -> Result<(), DispatchError> {
+		let dispatch_info = call.get_dispatch_info();
+		let weight: Balance = ChargeTransactionPayment::traditional_fee(0, &dispatch_info, 0)
+			.saturating_mul(count.into());
+
+		<Balances as NamedReservableCurrency<AccountId>>::reserve_named(
+			&id,
+			&(sponsor.into()),
+			weight.into(),
+		)
+	}
+
+	fn pay_for_call(
+		id: [u8; 16],
+		sponsor: <T as frame_system::Config>::AccountId,
+		call: <T as pallet_unq_scheduler::Config>::Call,
+	) -> Result<u128, DispatchError> {
+		let dispatch_info = call.get_dispatch_info();
+		let weight: Balance = ChargeTransactionPayment::traditional_fee(0, &dispatch_info, 0);
+		Ok(
+			<Balances as NamedReservableCurrency<AccountId>>::unreserve_named(
+				&id,
+				&(sponsor.into()),
+				weight.into(),
+			),
+		)
+	}
+
+	fn cancel_reserve(
+		id: [u8; 16],
+		sponsor: <T as frame_system::Config>::AccountId,
+	) -> Result<u128, DispatchError> {
+		Ok(
+			<Balances as NamedReservableCurrency<AccountId>>::unreserve_named(
+				&id,
+				&(sponsor.into()),
+				u128::MAX,
+			),
+		)
+	}
+}
+
 parameter_types! {
 	// 0x842899ECF380553E8a4de75bF534cdf6fBF64049
 	pub const HelpersContractAddress: H160 = H160([
@@ -1003,7 +1133,7 @@
 		// Unique Pallets
 		Inflation: pallet_inflation::{Pallet, Call, Storage} = 60,
 		Unique: pallet_unique::{Pallet, Call, Storage, Event<T>} = 61,
-		// Scheduler: pallet_unq_scheduler::{Pallet, Call, Storage, Event<T>} = 62,
+		Scheduler: pallet_unq_scheduler::{Pallet, Call, Storage, Event<T>} = 62,
 		// free = 63
 		Charging: pallet_charge_transaction::{Pallet, Call, Storage } = 64,
 		// ContractHelpers: pallet_contract_helpers::{Pallet, Call, Storage} = 65,
@@ -1073,6 +1203,15 @@
 	//pallet_contract_helpers::ContractHelpersExtension<Runtime>,
 	pallet_ethereum::FakeTransactionFinalizer<Runtime>,
 );
+
+pub type SignedExtraScheduler = (
+	frame_system::CheckSpecVersion<Runtime>,
+	frame_system::CheckGenesis<Runtime>,
+	frame_system::CheckEra<Runtime>,
+	frame_system::CheckNonce<Runtime>,
+	frame_system::CheckWeight<Runtime>,
+	// pallet_charge_transaction::ChargeTransactionPayment<Runtime>,
+);
 /// Unchecked extrinsic type as expected by this runtime.
 pub type UncheckedExtrinsic =
 	fp_self_contained::UncheckedExtrinsic<Address, Call, Signature, SignedExtra>;
modifiedruntime/unique/src/lib.rsdiffbeforeafterboth
--- a/runtime/unique/src/lib.rs
+++ b/runtime/unique/src/lib.rs
@@ -33,11 +33,20 @@
 
 use sp_runtime::{
 	Permill, Perbill, Percent, create_runtime_str, generic, impl_opaque_keys,
-	traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, AccountIdConversion, Zero},
-	transaction_validity::{TransactionSource, TransactionValidity},
-	ApplyExtrinsicResult, RuntimeAppPublic,
+	generic::Era,
+	traits::{
+		Applyable, BlockNumberProvider, Dispatchable, PostDispatchInfoOf, Saturating,
+		CheckedConversion, AccountIdLookup, BlakeTwo256, Block as BlockT, AccountIdConversion,
+		Zero, Member,
+	},
+	transaction_validity::{TransactionSource, TransactionValidity, TransactionValidityError},
+	ApplyExtrinsicResult, RuntimeAppPublic, SaturatedConversion, DispatchErrorWithPostInfo,
 };
 
+use fp_self_contained::{SelfContainedCall, CheckedSignature};
+use sp_std::{cmp::Ordering, marker::PhantomData};
+use pallet_unq_scheduler::DispatchCall;
+
 use sp_std::prelude::*;
 
 #[cfg(feature = "std")]
@@ -59,7 +68,7 @@
 	traits::{
 		tokens::currency::Currency as CurrencyT, OnUnbalanced as OnUnbalancedT, Everything,
 		Currency, ExistenceRequirement, Get, IsInVec, KeyOwnerProofSystem, LockIdentifier,
-		OnUnbalanced, Randomness, FindAuthor, ConstU32, Imbalance,
+		OnUnbalanced, Randomness, FindAuthor, ConstU32, Imbalance, PrivilegeCmp,
 	},
 	weights::{
 		constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},
@@ -86,18 +95,17 @@
 use smallvec::smallvec;
 use codec::{Encode, Decode};
 use fp_rpc::TransactionStatus;
-use sp_runtime::{
-	traits::{BlockNumberProvider, Dispatchable, PostDispatchInfoOf, Saturating},
-	transaction_validity::TransactionValidityError,
-	SaturatedConversion,
-};
 
 // pub use pallet_timestamp::Call as TimestampCall;
 
 // Polkadot imports
 use pallet_xcm::XcmPassthrough;
 use polkadot_parachain::primitives::Sibling;
-use up_data_structs::mapping::{CrossTokenAddressMapping, EvmTokenAddressMapping};
+use up_data_structs::{
+	CollectionId, TokenId, TokenData, Property, PropertyKeyPermission, CollectionLimits, 
+	CollectionStats, RpcCollection, 
+	mapping::{EvmTokenAddressMapping, CrossTokenAddressMapping}
+};
 use xcm::v1::{BodyId, Junction::*, MultiLocation, NetworkId, Junctions::*};
 use xcm_builder::{
 	AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter,
@@ -107,7 +115,6 @@
 	ParentIsPreset,
 };
 use xcm_executor::{Config, XcmExecutor, Assets};
-use sp_std::{marker::PhantomData};
 
 use xcm::latest::{
 	//	Xcm,
@@ -117,7 +124,6 @@
 	Error as XcmError,
 };
 use xcm_executor::traits::{MatchesFungible, WeightTrader};
-//use xcm_executor::traits::MatchesFungible;
 use sp_runtime::traits::CheckedConversion;
 
 use unique_runtime_common::{
@@ -395,7 +401,7 @@
 impl pallet_balances::Config for Runtime {
 	type MaxLocks = MaxLocks;
 	type MaxReserves = ();
-	type ReserveIdentifier = [u8; 8];
+	type ReserveIdentifier = [u8; 16];
 	/// The type for recording an account's balance.
 	type Balance = Balance;
 	/// The ubiquitous event type.
@@ -918,12 +924,145 @@
 	type BlockNumberProvider = RelayChainBlockNumberProvider<Runtime>;
 }
 
-// parameter_types! {
-// 	pub MaximumSchedulerWeight: Weight = Perbill::from_percent(50) *
-// 		RuntimeBlockWeights::get().max_block;
-// 	pub const MaxScheduledPerBlock: u32 = 50;
-// }
+parameter_types! {
+	pub MaximumSchedulerWeight: Weight = Perbill::from_percent(50) *
+		RuntimeBlockWeights::get().max_block;
+	pub const MaxScheduledPerBlock: u32 = 50;
+}
+
+parameter_types! {
+	pub const NoPreimagePostponement: Option<u32> = Some(10);
+	pub const Preimage: Option<u32> = Some(10);
+}
 
+/// Used the compare the privilege of an origin inside the scheduler.
+pub struct OriginPrivilegeCmp;
+impl PrivilegeCmp<OriginCaller> for OriginPrivilegeCmp {
+	fn cmp_privilege(_left: &OriginCaller, _right: &OriginCaller) -> Option<Ordering> {
+		Some(Ordering::Equal)
+	}
+}
+
+impl pallet_unq_scheduler::Config for Runtime {
+	type Event = Event;
+	type Origin = Origin;
+	type Currency = Balances;
+	type PalletsOrigin = OriginCaller;
+	type Call = Call;
+	type MaximumWeight = MaximumSchedulerWeight;
+	type ScheduleOrigin = EnsureSigned<AccountId>;
+	type MaxScheduledPerBlock = MaxScheduledPerBlock;
+	type WeightInfo = ();
+	type CallExecutor = SchedulerPaymentExecutor;
+	type OriginPrivilegeCmp = OriginPrivilegeCmp;
+	type PreimageProvider = ();
+	type NoPreimagePostponement = NoPreimagePostponement;
+}
+
+type ChargeTransactionPayment = pallet_charge_transaction::ChargeTransactionPayment<Runtime>;
+use frame_support::traits::NamedReservableCurrency;
+
+fn get_signed_extras(from: <Runtime as frame_system::Config>::AccountId) -> SignedExtraScheduler {
+	(
+		frame_system::CheckSpecVersion::<Runtime>::new(),
+		frame_system::CheckGenesis::<Runtime>::new(),
+		frame_system::CheckEra::<Runtime>::from(Era::Immortal),
+		frame_system::CheckNonce::<Runtime>::from(frame_system::Pallet::<Runtime>::account_nonce(
+			from,
+		)),
+		frame_system::CheckWeight::<Runtime>::new(),
+		// sponsoring transaction logic
+		// pallet_charge_transaction::ChargeTransactionPayment::<Runtime>::new(0),
+	)
+}
+
+pub struct SchedulerPaymentExecutor;
+impl<T: frame_system::Config + pallet_unq_scheduler::Config, SelfContainedSignedInfo>
+	DispatchCall<T, SelfContainedSignedInfo> for SchedulerPaymentExecutor
+where
+	<T as frame_system::Config>::Call: Member
+		+ Dispatchable<Origin = Origin, Info = DispatchInfo>
+		+ SelfContainedCall<SignedInfo = SelfContainedSignedInfo>
+		+ GetDispatchInfo
+		+ From<frame_system::Call<Runtime>>,
+	SelfContainedSignedInfo: Send + Sync + 'static,
+	Call: From<<T as frame_system::Config>::Call>
+		+ From<<T as pallet_unq_scheduler::Config>::Call>
+		+ SelfContainedCall<SignedInfo = SelfContainedSignedInfo>,
+	sp_runtime::AccountId32: From<<T as frame_system::Config>::AccountId>,
+{
+	fn dispatch_call(
+		signer: <T as frame_system::Config>::AccountId,
+		call: <T as pallet_unq_scheduler::Config>::Call,
+	) -> Result<
+		Result<PostDispatchInfo, DispatchErrorWithPostInfo<PostDispatchInfo>>,
+		TransactionValidityError,
+	> {
+		let dispatch_info = call.get_dispatch_info();
+		let extrinsic = fp_self_contained::CheckedExtrinsic::<
+			AccountId,
+			Call,
+			SignedExtraScheduler,
+			SelfContainedSignedInfo,
+		> {
+			signed:
+				CheckedSignature::<AccountId, SignedExtraScheduler, SelfContainedSignedInfo>::Signed(
+					signer.clone().into(),
+					get_signed_extras(signer.into()),
+				),
+			function: call.into(),
+		};
+
+		extrinsic.apply::<Runtime>(&dispatch_info, 0)
+	}
+
+	fn reserve_balance(
+		id: [u8; 16],
+		sponsor: <T as frame_system::Config>::AccountId,
+		call: <T as pallet_unq_scheduler::Config>::Call,
+		count: u32,
+	) -> Result<(), DispatchError> {
+		let dispatch_info = call.get_dispatch_info();
+		let weight: Balance = ChargeTransactionPayment::traditional_fee(0, &dispatch_info, 0)
+			.saturating_mul(count.into());
+
+		<Balances as NamedReservableCurrency<AccountId>>::reserve_named(
+			&id,
+			&(sponsor.into()),
+			weight.into(),
+		)
+	}
+
+	fn pay_for_call(
+		id: [u8; 16],
+		sponsor: <T as frame_system::Config>::AccountId,
+		call: <T as pallet_unq_scheduler::Config>::Call,
+	) -> Result<u128, DispatchError> {
+		let dispatch_info = call.get_dispatch_info();
+		let weight: Balance = ChargeTransactionPayment::traditional_fee(0, &dispatch_info, 0);
+		Ok(
+			<Balances as NamedReservableCurrency<AccountId>>::unreserve_named(
+				&id,
+				&(sponsor.into()),
+				weight.into(),
+			),
+		)
+	}
+
+	fn cancel_reserve(
+		id: [u8; 16],
+		sponsor: <T as frame_system::Config>::AccountId,
+	) -> Result<u128, DispatchError> {
+		Ok(
+			<Balances as NamedReservableCurrency<AccountId>>::unreserve_named(
+				&id,
+				&(sponsor.into()),
+				u128::MAX,
+			),
+		)
+	}
+}
+
 type EvmSponsorshipHandler = (
 	UniqueEthSponsorshipHandler<Runtime>,
 	pallet_evm_contract_helpers::HelpersContractSponsoring<Runtime>,
@@ -933,18 +1072,6 @@
 	//pallet_contract_helpers::ContractSponsorshipHandler<Runtime>,
 	pallet_evm_transaction_payment::BridgeSponsorshipHandler<Runtime>,
 );
-
-// impl pallet_unq_scheduler::Config for Runtime {
-// 	type Event = Event;
-// 	type Origin = Origin;
-// 	type PalletsOrigin = OriginCaller;
-// 	type Call = Call;
-// 	type MaximumWeight = MaximumSchedulerWeight;
-// 	type ScheduleOrigin = EnsureSigned<AccountId>;
-// 	type MaxScheduledPerBlock = MaxScheduledPerBlock;
-// 	type SponsorshipHandler = SponsorshipHandler;
-// 	type WeightInfo = ();
-// }
 
 impl pallet_evm_transaction_payment::Config for Runtime {
 	type EvmSponsorshipHandler = EvmSponsorshipHandler;
@@ -1008,7 +1135,7 @@
 		// Unique Pallets
 		Inflation: pallet_inflation::{Pallet, Call, Storage} = 60,
 		Unique: pallet_unique::{Pallet, Call, Storage, Event<T>} = 61,
-		// Scheduler: pallet_unq_scheduler::{Pallet, Call, Storage, Event<T>} = 62,
+		Scheduler: pallet_unq_scheduler::{Pallet, Call, Storage, Event<T>} = 62,
 		// free = 63
 		Charging: pallet_charge_transaction::{Pallet, Call, Storage } = 64,
 		// ContractHelpers: pallet_contract_helpers::{Pallet, Call, Storage} = 65,
@@ -1078,6 +1205,14 @@
 	//pallet_contract_helpers::ContractHelpersExtension<Runtime>,
 	pallet_ethereum::FakeTransactionFinalizer<Runtime>,
 );
+pub type SignedExtraScheduler = (
+	frame_system::CheckSpecVersion<Runtime>,
+	frame_system::CheckGenesis<Runtime>,
+	frame_system::CheckEra<Runtime>,
+	frame_system::CheckNonce<Runtime>,
+	frame_system::CheckWeight<Runtime>,
+	// pallet_charge_transaction::ChargeTransactionPayment<Runtime>,
+);
 /// Unchecked extrinsic type as expected by this runtime.
 pub type UncheckedExtrinsic =
 	fp_self_contained::UncheckedExtrinsic<Address, Call, Signature, SignedExtra>;
modifiedtests/package.jsondiffbeforeafterboth
--- a/tests/package.json
+++ b/tests/package.json
@@ -68,6 +68,8 @@
     "testOverflow": "mocha --timeout 9999999 -r ts-node/register ./**/overflow.test.ts",
     "testSetVariableMetadataSponsoringRateLimit": "mocha --timeout 9999999 -r ts-node/register ./**/setVariableMetadataSponsoringRateLimit.test.ts",
     "testInflation": "mocha --timeout 9999999 -r ts-node/register ./**/inflation.test.ts",
+    "testScheduler": "mocha --timeout 9999999 -r ts-node/register ./**/scheduler.test.ts",
+    "testSchedulingEVM": "mocha --timeout 9999999 -r ts-node/register ./**/eth/scheduling.test.ts",
     "testXcmTransfer": "mocha --timeout 9999999 -r ts-node/register ./**/xcmTransfer.test.ts",
     "testPalletPresence": "mocha --timeout 9999999 -r ts-node/register ./**/pallet-presence.test.ts",
     "testBlockProduction": "mocha --timeout 9999999 -r ts-node/register ./**/block-production.test.ts",
addedtests/src/eth/scheduling.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/eth/scheduling.test.ts
@@ -0,0 +1,55 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+import {expect} from 'chai';
+import {createEthAccountWithBalance, deployFlipper, GAS_ARGS, itWeb3, subToEth, transferBalanceToEth} from './util/helpers';
+import {scheduleExpectSuccess, waitNewBlocks} from '../util/helpers';
+import privateKey from '../substrate/privateKey';
+
+describe('Scheduing EVM smart contracts', () => {
+  itWeb3('Successfully schedules and periodically executes an EVM contract', async ({api, web3}) => {
+    const deployer = await createEthAccountWithBalance(api, web3);
+    const flipper = await deployFlipper(web3, deployer);
+    const initialValue = await flipper.methods.getValue().call();
+    const alice = privateKey('//Alice');
+    await transferBalanceToEth(api, alice, subToEth(alice.address));
+
+    {
+      const tx = api.tx.evm.call(
+        subToEth(alice.address),
+        flipper.options.address,
+        flipper.methods.flip().encodeABI(),
+        '0',
+        GAS_ARGS.gas,
+        await web3.eth.getGasPrice(),
+        null,
+        null,
+        [],
+      );
+      const waitForBlocks = 4;
+      const periodBlocks = 2;
+
+      await scheduleExpectSuccess(tx, alice, waitForBlocks, '0x' + '0'.repeat(32), periodBlocks, 2);
+      expect(await flipper.methods.getValue().call()).to.be.equal(initialValue);
+      
+      await waitNewBlocks(waitForBlocks - 1);
+      expect(await flipper.methods.getValue().call()).to.be.not.equal(initialValue);
+  
+      await waitNewBlocks(periodBlocks);
+      expect(await flipper.methods.getValue().call()).to.be.equal(initialValue);
+    }
+  });
+});
\ No newline at end of file
modifiedtests/src/pallet-presence.test.tsdiffbeforeafterboth
--- a/tests/src/pallet-presence.test.ts
+++ b/tests/src/pallet-presence.test.ts
@@ -50,7 +50,7 @@
   'unique',
   'nonfungible',
   'refungible',
-  //'scheduler',
+  'scheduler',
   'charging',
 ];
 
modifiedtests/src/scheduler.test.tsdiffbeforeafterboth
--- a/tests/src/scheduler.test.ts
+++ b/tests/src/scheduler.test.ts
@@ -14,32 +14,197 @@
 // You should have received a copy of the GNU General Public License
 // along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
 
-import chai from 'chai';
+import chai, {expect} from 'chai';
 import chaiAsPromised from 'chai-as-promised';
 import privateKey from './substrate/privateKey';
-import usingApi from './substrate/substrate-api';
 import {
+  default as usingApi, 
+  submitTransactionAsync,
+} from './substrate/substrate-api';
+import {
   createItemExpectSuccess,
   createCollectionExpectSuccess,
   scheduleTransferExpectSuccess,
+  scheduleTransferAndWaitExpectSuccess,
   setCollectionSponsorExpectSuccess,
   confirmSponsorshipExpectSuccess,
+  findUnusedAddress,
+  UNIQUE,
+  enablePublicMintingExpectSuccess,
+  addToAllowListExpectSuccess,
+  waitNewBlocks,
+  normalizeAccountId,
+  getTokenOwner,
+  getGenericResult,
+  scheduleTransferFundsPeriodicExpectSuccess,
+  getFreeBalance,
+  confirmSponsorshipByKeyExpectSuccess,
+  scheduleExpectFailure,
 } from './util/helpers';
+import {IKeyringPair} from '@polkadot/types/types';
 
 chai.use(chaiAsPromised);
 
-describe.skip('Integration Test scheduler base transaction', () => {
-  it('User can transfer owned token with delay (scheduler)', async () => {
+describe.skip('Scheduling token and balance transfers', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let scheduledIdBase: string;
+  let scheduledIdSlider: number;
+
+  before(async() => {
     await usingApi(async () => {
-      const alice = privateKey('//Alice');
-      const bob = privateKey('//Bob');
-      // nft
+      alice = privateKey('//Alice');
+      bob = privateKey('//Bob');
+    });
+
+    scheduledIdBase = '0x' + '0'.repeat(31);
+    scheduledIdSlider = 0;
+  });
+
+  // Loop scheduledId around 10. Unless there are concurrent tasks with long periods/repetitions, tests' tasks' ids shouldn't ovelap.
+  function makeScheduledId(): string {
+    return scheduledIdBase + ((scheduledIdSlider++) % 10);
+  }
+
+  it('Can schedule a transfer of an owned token with delay', async () => {
+    await usingApi(async () => {
       const nftCollectionId = await createCollectionExpectSuccess();
       const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');
       await setCollectionSponsorExpectSuccess(nftCollectionId, alice.address);
       await confirmSponsorshipExpectSuccess(nftCollectionId);
 
-      await scheduleTransferExpectSuccess(nftCollectionId, newNftTokenId, alice, bob, 1, 4);
+      await scheduleTransferAndWaitExpectSuccess(nftCollectionId, newNftTokenId, alice, bob, 1, 4, makeScheduledId());
+    });
+  });
+
+  it('Can transfer funds periodically', async () => {
+    await usingApi(async () => {
+      const waitForBlocks = 4;
+      const period = 2;
+      await scheduleTransferFundsPeriodicExpectSuccess(1n * UNIQUE, alice, bob, waitForBlocks, makeScheduledId(), period, 2);
+      const bobsBalanceBefore = await getFreeBalance(bob);
+
+      // discounting already waited-for operations
+      await waitNewBlocks(waitForBlocks - 2);
+      const bobsBalanceAfterFirst = await getFreeBalance(bob);
+      expect(bobsBalanceAfterFirst > bobsBalanceBefore).to.be.true;
+
+      await waitNewBlocks(period);
+      const bobsBalanceAfterSecond = await getFreeBalance(bob);
+      expect(bobsBalanceAfterSecond > bobsBalanceAfterFirst).to.be.true;
+    });
+  });
+
+  it('Can sponsor scheduling a transaction', async () => {
+    const collectionId = await createCollectionExpectSuccess();
+    await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+    await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
+
+    await usingApi(async () => {
+      const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);
+
+      const bobBalanceBefore = await getFreeBalance(bob);
+      const waitForBlocks = 4;
+      // no need to wait to check, fees must be deducted on scheduling, immediately
+      await scheduleTransferExpectSuccess(collectionId, tokenId, alice, bob, 0, waitForBlocks, makeScheduledId());
+      const bobBalanceAfter = await getFreeBalance(bob);
+      // expect(aliceBalanceAfter == aliceBalanceBefore).to.be.true;
+      expect(bobBalanceAfter < bobBalanceBefore).to.be.true;
+      // wait for sequentiality matters
+      await waitNewBlocks(waitForBlocks - 1);
+    });
+  });
+
+  it('Schedules and dispatches a transaction even if the caller has no funds at the time of the dispatch', async () => {
+    await usingApi(async (api) => {
+      // Find an empty, unused account
+      const zeroBalance = await findUnusedAddress(api);
+
+      const collectionId = await createCollectionExpectSuccess();
+
+      // Add zeroBalance address to allow list
+      await enablePublicMintingExpectSuccess(alice, collectionId);
+      await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);
+
+      // Grace zeroBalance with money, enough to cover future transactions
+      const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);
+      await submitTransactionAsync(alice, balanceTx);
+
+      // Mint a fresh NFT
+      const tokenId = await createItemExpectSuccess(zeroBalance, collectionId, 'NFT');
+
+      // Schedule transfer of the NFT a few blocks ahead
+      const waitForBlocks = 5;
+      await scheduleTransferExpectSuccess(collectionId, tokenId, zeroBalance, alice, 1, waitForBlocks, makeScheduledId());
+
+      // Get rid of the account's funds before the scheduled transaction takes place
+      const balanceTx2 = api.tx.balances.transfer(alice.address, UNIQUE * 68n / 100n);
+      const events = await submitTransactionAsync(zeroBalance, balanceTx2);
+      expect(getGenericResult(events).success).to.be.true;
+      /*const emptyBalanceTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0); // do not null reserved?
+      const sudoTx = api.tx.sudo.sudo(emptyBalanceTx as any);
+      const events = await submitTransactionAsync(alice, sudoTx);
+      expect(getGenericResult(events).success).to.be.true;*/
+
+      // Wait for a certain number of blocks, discarding the ones that already happened while accepting the late transactions
+      await waitNewBlocks(waitForBlocks - 3);
+
+      expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(alice.address));
+    });
+  });
+
+  it('Sponsor going bankrupt does not impact a scheduled transaction', async () => {
+    const collectionId = await createCollectionExpectSuccess();
+
+    await usingApi(async (api) => {
+      const zeroBalance = await findUnusedAddress(api);
+      const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);
+      await submitTransactionAsync(alice, balanceTx);
+
+      await setCollectionSponsorExpectSuccess(collectionId, zeroBalance.address);
+      await confirmSponsorshipByKeyExpectSuccess(collectionId, zeroBalance);
+
+      const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);
+
+      const waitForBlocks = 5;
+      await scheduleTransferExpectSuccess(collectionId, tokenId, alice, zeroBalance, 1, waitForBlocks, makeScheduledId());
+
+      const emptyBalanceSponsorTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0);
+      const sudoTx = api.tx.sudo.sudo(emptyBalanceSponsorTx as any);
+      const events = await submitTransactionAsync(alice, sudoTx);
+      expect(getGenericResult(events).success).to.be.true;
+
+      // Wait for a certain number of blocks, save for the ones that already happened while accepting the late transactions
+      await waitNewBlocks(waitForBlocks - 3);
+
+      expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(zeroBalance.address));
+    });
+  });
+
+  it.skip('Exceeding sponsor rate limit without having enough funds prevents scheduling a periodic transaction', async () => {
+    const collectionId = await createCollectionExpectSuccess();
+    await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+    await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
+
+    await usingApi(async (api) => {
+      const zeroBalance = await findUnusedAddress(api);
+
+      await enablePublicMintingExpectSuccess(alice, collectionId);
+      await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);
+
+      const bobBalanceBefore = await getFreeBalance(bob);
+
+      const createData = {nft: {const_data: [], variable_data: []}};
+      const creationTx = api.tx.unique.createItem(collectionId, normalizeAccountId(zeroBalance), createData as any);
+
+      /*const badTransaction = async function () {
+        await submitTransactionExpectFailAsync(zeroBalance, zeroToAlice);
+      };
+      await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees');*/
+
+      await scheduleExpectFailure(creationTx, zeroBalance, 3, makeScheduledId(), 1, 3);
+
+      expect(await getFreeBalance(bob)).to.be.equal(bobBalanceBefore);
     });
   });
 });
modifiedtests/src/util/helpers.tsdiffbeforeafterboth
--- a/tests/src/util/helpers.ts
+++ b/tests/src/util/helpers.ts
@@ -630,10 +630,16 @@
 }
 
 export async function confirmSponsorshipExpectSuccess(collectionId: number, senderSeed = '//Alice') {
+  await usingApi(async () => {
+    const sender = privateKey(senderSeed);
+    await confirmSponsorshipByKeyExpectSuccess(collectionId, sender);
+  });
+}
+
+export async function confirmSponsorshipByKeyExpectSuccess(collectionId: number, sender: IKeyringPair) {
   await usingApi(async (api) => {
 
     // Run the transaction
-    const sender = privateKey(senderSeed);
     const tx = api.tx.unique.confirmSponsorship(collectionId);
     const events = await submitTransactionAsync(sender, tx);
     const result = getGenericResult(events);
@@ -899,7 +905,7 @@
 }
 
 /* eslint no-async-promise-executor: "off" */
-async function getBlockNumber(api: ApiPromise): Promise<number> {
+export async function getBlockNumber(api: ApiPromise): Promise<number> {
   return new Promise<number>(async (resolve) => {
     const unsubscribe = await api.rpc.chain.subscribeNewHeads((head) => {
       unsubscribe();
@@ -935,29 +941,76 @@
 }
 
 export async function
-scheduleTransferExpectSuccess(
-  collectionId: number,
-  tokenId: number,
+scheduleExpectSuccess(
+  operationTx: any,
   sender: IKeyringPair,
-  recipient: IKeyringPair,
-  value: number | bigint = 1,
   blockSchedule: number,
+  scheduledId: string,
+  period = 1,
+  repetitions = 1,
 ) {
   await usingApi(async (api: ApiPromise) => {
     const blockNumber: number | undefined = await getBlockNumber(api);
     const expectedBlockNumber = blockNumber + blockSchedule;
 
     expect(blockNumber).to.be.greaterThan(0);
-    const transferTx = api.tx.unique.transfer(normalizeAccountId(recipient.address), collectionId, tokenId, value);
-    const scheduleTx = api.tx.scheduler.schedule(expectedBlockNumber, null, 0, transferTx as any);
+    const scheduleTx = api.tx.scheduler.scheduleNamed( // schedule
+      scheduledId,
+      expectedBlockNumber, 
+      repetitions > 1 ? [period, repetitions] : null, 
+      0, 
+      {value: operationTx as any},
+    );
+
+    const events = await submitTransactionAsync(sender, scheduleTx);
+    expect(getGenericResult(events).success).to.be.true;
+  });
+}
+
+export async function
+scheduleExpectFailure(
+  operationTx: any,
+  sender: IKeyringPair,
+  blockSchedule: number,
+  scheduledId: string,
+  period = 1,
+  repetitions = 1,
+) {
+  await usingApi(async (api: ApiPromise) => {
+    const blockNumber: number | undefined = await getBlockNumber(api);
+    const expectedBlockNumber = blockNumber + blockSchedule;
+
+    expect(blockNumber).to.be.greaterThan(0);
+    const scheduleTx = api.tx.scheduler.scheduleNamed( // schedule
+      scheduledId,
+      expectedBlockNumber, 
+      repetitions <= 1 ? null : [period, repetitions], 
+      0, 
+      {value: operationTx as any},
+    );
+
+    //const events = 
+    await expect(submitTransactionExpectFailAsync(sender, scheduleTx)).to.be.rejected;
+    //expect(getGenericResult(events).success).to.be.false;
+  });
+}
 
-    await submitTransactionAsync(sender, scheduleTx);
+export async function
+scheduleTransferAndWaitExpectSuccess(
+  collectionId: number,
+  tokenId: number,
+  sender: IKeyringPair,
+  recipient: IKeyringPair,
+  value: number | bigint = 1,
+  blockSchedule: number,
+  scheduledId: string,
+) {
+  await usingApi(async (api: ApiPromise) => {
+    await scheduleTransferExpectSuccess(collectionId, tokenId, sender, recipient, value, blockSchedule, scheduledId);
 
     const recipientBalanceBefore = (await api.query.system.account(recipient.address)).data.free.toBigInt();
 
-    expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(sender.address));
-
-    // sleep for 4 blocks
+    // sleep for n + 1 blocks
     await waitNewBlocks(blockSchedule + 1);
 
     const recipientBalanceAfter = (await api.query.system.account(recipient.address)).data.free.toBigInt();
@@ -967,6 +1020,45 @@
   });
 }
 
+export async function
+scheduleTransferExpectSuccess(
+  collectionId: number,
+  tokenId: number,
+  sender: IKeyringPair,
+  recipient: IKeyringPair,
+  value: number | bigint = 1,
+  blockSchedule: number,
+  scheduledId: string,
+) {
+  await usingApi(async (api: ApiPromise) => {
+    const transferTx = api.tx.unique.transfer(normalizeAccountId(recipient.address), collectionId, tokenId, value);
+
+    await scheduleExpectSuccess(transferTx, sender, blockSchedule, scheduledId);
+
+    expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(sender.address));
+  });
+}
+
+export async function
+scheduleTransferFundsPeriodicExpectSuccess(
+  amount: bigint,
+  sender: IKeyringPair,
+  recipient: IKeyringPair,
+  blockSchedule: number,
+  scheduledId: string,
+  period: number,
+  repetitions: number,
+) {
+  await usingApi(async (api: ApiPromise) => {
+    const transferTx = api.tx.balances.transfer(recipient.address, amount);
+
+    const balanceBefore = await getFreeBalance(recipient);
+    
+    await scheduleExpectSuccess(transferTx, sender, blockSchedule, scheduledId, period, repetitions);
+
+    expect(await getFreeBalance(recipient)).to.be.equal(balanceBefore);
+  });
+}
 
 export async function
 transferExpectSuccess(