I want to start a function automatically after a certain period of time in our chain. How can I do it?
3 Answers
You could use the pallet-scheduler
.
pallet_scheduler::Pallet::<T>::schedule(
Origin::root(), // Depending on what you have configured as `ScheduleOrigin` in the runtime for scheduler
when,
None, // or Some if it should be periodic
priority,
Box::new(Call::Whatever {}), // The actual call that should be scheduled
);
You can also program this on your own using on_initialize
:
fn on_initialize(now: T::BlockNumber) -> Weight {
if now == expected_number {
// do whatever you want to do.
}
}
-
-
@bkchr Can you please suggest the resources to learn about Hooks? Oct 5, 2022 at 15:22
There exists the scheduler
pallet which archives this. The downside is that only the ScheduleOrigin
may schedule calls. But your question does not state who should be able to schedule calls.
The signature looks like this:
pub fn schedule(
origin: OriginFor<T>,
when: T::BlockNumber,
maybe_periodic: Option<schedule::Period<T::BlockNumber>>,
priority: schedule::Priority,
call: Box<CallOrHashOf<T>>,
) -> DispatchResult {
The call may be executed by the scheduler at any block after when
, giving no guarantees if it ever happens. Reasons for non-execution could be full blocks, full schedules or an over-weight call. On successful execution a Dispatched
event will be emitted for your convenience.
Usage in tests looks like this:
// Some example call to be scheduled.
let call = Box::new(RuntimeCall::Logger(LoggerCall::log {
i: 69,
weight: Weight::from_ref_time(10),
}));
assert_ok!(Scheduler::schedule(RuntimeOrigin::root(), 4, None, 127, call));
Another way would be to write a pallet and utilize its on_initialize
/on_idle
hooks. In these hooks you can then check the block number and run a specific task. The scheduler
uses the same mechanism.
Like @bkchr mentioned, you can use on_initialize
hook.
Here is a concrete example in the treasury pallet:
impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {
fn on_initialize(n: T::BlockNumber) -> Weight {
// Check to see if we should spend some funds!
if (n % T::SpendPeriod::get()).is_zero() {
Self::spend_funds()
} else {
Weight::zero()
}
}
}
spend_funds()
is only called on the T::SpendPeriod::get()
- which in Polkadot's case, is every 24 days.
-
-
Yes, you can look here: paritytech.github.io/substrate/master/frame_support/traits/… and you can also do a search in the Substrate repo for specific hooks - they are used extensively.– Bruno ♦Oct 5, 2022 at 18:27