5

I want to start a function automatically after a certain period of time in our chain. How can I do it?

3 Answers 3

5

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.
    }
}
2
  • Uh, you were faster 😂 Oct 4, 2022 at 19:17
  • @bkchr Can you please suggest the resources to learn about Hooks? Oct 5, 2022 at 15:22
2

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.

2

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.

2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.