Attribute Macro frame_support::pallet_macros::call

source ·
#[call]
Expand description

Allows a pallet to declare a set of functions as a dispatchable extrinsic.

In slightly simplified terms, this macro declares the set of “transactions” of a pallet.

The exact definition of extrinsic can be found in sp_runtime::generic::UncheckedExtrinsic.

A dispatchable is a common term in FRAME, referring to process of constructing a function, and dispatching it with the correct inputs. This is commonly used with extrinsics, for example “an extrinsic has been dispatched”. See sp_runtime::traits::Dispatchable and crate::traits::UnfilteredDispatchable.

§Call Enum

The macro is called call (rather than #[pallet::extrinsics]) because of the generation of a enum Call. This enum contains only the encoding of the function arguments of the dispatchable, alongside the information needed to route it to the correct function.

#[frame_support::pallet(dev_mode)]
pub mod custom_pallet {
    #[pallet::call]
    impl<T: Config> Pallet<T> {
        pub fn some_dispatchable(_origin: OriginFor<T>, _input: u32) -> DispatchResult {
            Ok(())
        }
        pub fn other(_origin: OriginFor<T>, _input: u64) -> DispatchResult {
            Ok(())
        }
    }

    // generates something like:
    // enum Call<T: Config> {
    //  some_dispatchable { input: u32 }
    //  other { input: u64 }
    // }
}

fn main() {
    construct_runtime! {
        pub enum Runtime {
            System: frame_system,
            Custom: custom_pallet
        }
    }

    let origin: RuntimeOrigin = frame_system::RawOrigin::Signed(10).into();
    // calling into a dispatchable from within the runtime is simply a function call.
        let _ = custom_pallet::Pallet::<Runtime>::some_dispatchable(origin.clone(), 10);

    // calling into a dispatchable from the outer world involves constructing the bytes of
    let call = custom_pallet::Call::<Runtime>::some_dispatchable { input: 10 };
    let _ = call.clone().dispatch_bypass_filter(origin);

    // the routing of a dispatchable is simply done through encoding of the `Call` enum,
    // which is the index of the variant, followed by the arguments.
    assert_eq!(call.encode(), vec![0u8, 10, 0, 0, 0]);

    // notice how in the encoding of the second function, the first byte is different and
    // referring to the second variant of `enum Call`.
    let call = custom_pallet::Call::<Runtime>::other { input: 10 };
    assert_eq!(call.encode(), vec![1u8, 10, 0, 0, 0, 0, 0, 0, 0]);
}

Further properties of dispatchable functions are as follows:

WARNING: modifying dispatchables, changing their order (i.e. using call_index), removing some, etc., must be done with care. This will change the encoding of the , and the call can be stored on-chain (e.g. in pallet-scheduler). Thus, migration might be needed. This is why the use of call_index is mandatory by default in FRAME.

§Default Behavior

If no #[pallet::call] exists, then a default implementation corresponding to the following code is automatically generated:

#[frame_support::pallet(dev_mode)]
mod pallet {
	#[pallet::pallet]
	pub struct Pallet<T>(_);

	#[pallet::call] // <- automatically generated
	impl<T: Config> Pallet<T> {} // <- automatically generated

	#[pallet::config]
	pub trait Config: frame_system::Config {}
}

Documentation for this macro can be found at frame_support::pallet_macros::call.