#[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.
The macro also ensures that the extrinsic when invoked will be wrapped via
frame_support::storage::with_storage_layer
to make it transactional. Thus if the
extrinsic returns with an error any state changes that had already occurred will be
rolled back.
#[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:
- Unless if annotated by
dev_mode
, it must containweight
to denote the pre-dispatch weight consumed. - The dispatchable must declare its index via
call_index
, which can override the position of a function inenum Call
. - The first argument is always an
OriginFor
(orT::RuntimeOrigin
). - The return type is always
crate::dispatch::DispatchResult
(orcrate::dispatch::DispatchResultWithPostInfo
).
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 call,
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.
§Weight info
Each call needs to define a weight.
-
The weight can be defined explicitly using the attribute
#[pallet::weight($expr)]
(Note that argument of the call are available inside the expression). -
Or it can be defined implicitly, the weight info for the calls needs to be specified in the call attribute:
#[pallet::call(weight = $WeightInfo)]
, then each call that doesn’t have explicit weight will use$WeightInfo::$call_name
as the weight. -
Or it can be simply ignored when the pallet is in
dev_mode
.
#[frame_support::pallet]
mod pallet {
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config: frame_system::Config {
/// Type for specifying dispatchable weights.
type WeightInfo: WeightInfo;
}
/// The `WeightInfo` trait defines weight functions for dispatchable calls.
pub trait WeightInfo {
fn do_something() -> Weight;
fn do_something_else() -> Weight;
}
#[pallet::call(weight = <T as Config>::WeightInfo)]
impl<T: Config> Pallet<T> {
// Explicit weight definition using `#[pallet::weight(...)]`
#[pallet::weight(<T as Config>::WeightInfo::do_something())]
#[pallet::call_index(0)]
pub fn do_something(
origin: OriginFor<T>,
foo: u32,
) -> DispatchResult {
// Function logic here
Ok(())
}
// Implicit weight definition, the macro looks up to the weight info defined in
// `#[pallet::call(weight = $WeightInfo)]` attribute. Then use
// `$WeightInfo::do_something_else` as the weight function.
#[pallet::call_index(1)]
pub fn do_something_else(
origin: OriginFor<T>,
bar: u64,
) -> DispatchResult {
// Function logic here
Ok(())
}
}
}
§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 {}
}
§Note on deprecation of Calls
- Usage of
deprecated
attribute will propagate deprecation information to the pallet metadata where the item was declared. - For general usage examples of
deprecated
attribute please refer to https://doc.rust-lang.org/nightly/reference/attributes/diagnostics.html#the-deprecated-attribute - Usage of
allow(deprecated)
on the item will propagate this attribute to the generated code. - If the item is annotated with
deprecated
attribute then the generated code will be automatically annotated withallow(deprecated)
Documentation for this macro can be found at frame_support::pallet_macros::call
.