diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs
index ccff5488c93be..cd30946ae7855 100644
--- a/frame/support/procedural/src/lib.rs
+++ b/frame/support/procedural/src/lib.rs
@@ -445,6 +445,34 @@ pub fn construct_runtime(input: TokenStream) -> TokenStream {
/// pallet. Otherwise it implements `StorageInfoTrait` for the pallet using the
/// `PartialStorageInfoTrait` implementation of storages.
///
+/// ## Dev Mode (`#[pallet(dev_mode)]`)
+///
+/// Specifying the argument `dev_mode` will allow you to enable dev mode for a pallet. The aim
+/// of dev mode is to loosen some of the restrictions and requirements placed on production
+/// pallets for easy tinkering and development. Dev mode pallets should not be used in
+/// production. Enabling dev mode has the following effects:
+///
+/// * Weights no longer need to be specified on every `#[pallet::call]` declaration. By default, dev
+/// mode pallets will assume a weight of zero (`0`) if a weight is not specified. This is
+/// equivalent to specifying `#[weight(0)]` on all calls that do not specify a weight.
+/// * All storages are marked as unbounded, meaning you do not need to implement `MaxEncodedLen` on
+/// storage types. This is equivalent to specifying `#[pallet::unbounded]` on all storage type
+/// definitions.
+///
+/// Note that the `dev_mode` argument can only be supplied to the `#[pallet]` or
+/// `#[frame_support::pallet]` attribute macro that encloses your pallet module. This argument
+/// cannot be specified anywhere else, including but not limited to the `#[pallet::pallet]`
+/// attribute macro.
+///
+///
+/// WARNING:
+/// You should not deploy or use dev mode pallets in production. Doing so can break your chain
+/// and therefore should never be done. Once you are done tinkering, you should remove the
+/// 'dev_mode' argument from your #[pallet] declaration and fix any compile errors before
+/// attempting to use your pallet in a production scenario.
+///
+///
/// See `frame_support::pallet` docs for more info.
#[proc_macro_attribute]
pub fn pallet(attr: TokenStream, item: TokenStream) -> TokenStream {
diff --git a/frame/support/procedural/src/pallet/mod.rs b/frame/support/procedural/src/pallet/mod.rs
index ff9f122867746..3f85be81c1f7d 100644
--- a/frame/support/procedural/src/pallet/mod.rs
+++ b/frame/support/procedural/src/pallet/mod.rs
@@ -31,20 +31,30 @@ mod parse;
pub use parse::Def;
use syn::spanned::Spanned;
+mod keyword {
+ syn::custom_keyword!(dev_mode);
+}
+
pub fn pallet(
attr: proc_macro::TokenStream,
item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
+ let mut dev_mode = false;
if !attr.is_empty() {
- let msg =
- "Invalid pallet macro call: expected no attributes, e.g. macro call must be just \
- `#[frame_support::pallet]` or `#[pallet]`";
- let span = proc_macro2::TokenStream::from(attr).span();
- return syn::Error::new(span, msg).to_compile_error().into()
+ if let Ok(_) = syn::parse::(attr.clone()) {
+ dev_mode = true;
+ } else {
+ let msg = "Invalid pallet macro call: unexpected attribute. Macro call must be \
+ bare, such as `#[frame_support::pallet]` or `#[pallet]`, or must specify the \
+ `dev_mode` attribute, such as `#[frame_support::pallet(dev_mode)]` or \
+ #[pallet(dev_mode)].";
+ let span = proc_macro2::TokenStream::from(attr).span();
+ return syn::Error::new(span, msg).to_compile_error().into()
+ }
}
let item = syn::parse_macro_input!(item as syn::ItemMod);
- match parse::Def::try_from(item) {
+ match parse::Def::try_from(item, dev_mode) {
Ok(def) => expand::expand(def).into(),
Err(e) => e.to_compile_error().into(),
}
diff --git a/frame/support/procedural/src/pallet/parse/call.rs b/frame/support/procedural/src/pallet/parse/call.rs
index f7b2c9544d831..fbca9a52c767c 100644
--- a/frame/support/procedural/src/pallet/parse/call.rs
+++ b/frame/support/procedural/src/pallet/parse/call.rs
@@ -144,6 +144,7 @@ impl CallDef {
attr_span: proc_macro2::Span,
index: usize,
item: &mut syn::Item,
+ dev_mode: bool,
) -> syn::Result {
let item_impl = if let syn::Item::Impl(item) = item {
item
@@ -213,6 +214,14 @@ impl CallDef {
},
);
+ if weight_attrs.is_empty() && dev_mode {
+ // inject a default O(1) weight when dev mode is enabled and no weight has
+ // been specified on the call
+ let empty_weight: syn::Expr = syn::parse(quote::quote!(0).into())
+ .expect("we are parsing a quoted string; qed");
+ weight_attrs.push(FunctionAttr::Weight(empty_weight));
+ }
+
if weight_attrs.len() != 1 {
let msg = if weight_attrs.is_empty() {
"Invalid pallet::call, requires weight attribute i.e. `#[pallet::weight($expr)]`"
diff --git a/frame/support/procedural/src/pallet/parse/mod.rs b/frame/support/procedural/src/pallet/parse/mod.rs
index 8b4ab154b306a..f91159248281c 100644
--- a/frame/support/procedural/src/pallet/parse/mod.rs
+++ b/frame/support/procedural/src/pallet/parse/mod.rs
@@ -59,10 +59,11 @@ pub struct Def {
pub type_values: Vec,
pub frame_system: syn::Ident,
pub frame_support: syn::Ident,
+ pub dev_mode: bool,
}
impl Def {
- pub fn try_from(mut item: syn::ItemMod) -> syn::Result {
+ pub fn try_from(mut item: syn::ItemMod, dev_mode: bool) -> syn::Result {
let frame_system = generate_crate_access_2018("frame-system")?;
let frame_support = generate_crate_access_2018("frame-support")?;
@@ -106,7 +107,7 @@ impl Def {
hooks = Some(m);
},
Some(PalletAttr::RuntimeCall(span)) if call.is_none() =>
- call = Some(call::CallDef::try_from(span, index, item)?),
+ call = Some(call::CallDef::try_from(span, index, item, dev_mode)?),
Some(PalletAttr::Error(span)) if error.is_none() =>
error = Some(error::ErrorDef::try_from(span, index, item)?),
Some(PalletAttr::RuntimeEvent(span)) if event.is_none() =>
@@ -124,7 +125,7 @@ impl Def {
Some(PalletAttr::Inherent(_)) if inherent.is_none() =>
inherent = Some(inherent::InherentDef::try_from(index, item)?),
Some(PalletAttr::Storage(span)) =>
- storages.push(storage::StorageDef::try_from(span, index, item)?),
+ storages.push(storage::StorageDef::try_from(span, index, item, dev_mode)?),
Some(PalletAttr::ValidateUnsigned(_)) if validate_unsigned.is_none() => {
let v = validate_unsigned::ValidateUnsignedDef::try_from(index, item)?;
validate_unsigned = Some(v);
@@ -173,6 +174,7 @@ impl Def {
type_values,
frame_system,
frame_support,
+ dev_mode,
};
def.check_instance_usage()?;
diff --git a/frame/support/procedural/src/pallet/parse/storage.rs b/frame/support/procedural/src/pallet/parse/storage.rs
index b16ff05803d98..8b551ab31d6c3 100644
--- a/frame/support/procedural/src/pallet/parse/storage.rs
+++ b/frame/support/procedural/src/pallet/parse/storage.rs
@@ -678,6 +678,7 @@ impl StorageDef {
attr_span: proc_macro2::Span,
index: usize,
item: &mut syn::Item,
+ dev_mode: bool,
) -> syn::Result {
let item = if let syn::Item::Type(item) = item {
item
@@ -686,9 +687,11 @@ impl StorageDef {
};
let attrs: Vec = helper::take_item_pallet_attrs(&mut item.attrs)?;
- let PalletStorageAttrInfo { getter, rename_as, unbounded, whitelisted } =
+ let PalletStorageAttrInfo { getter, rename_as, mut unbounded, whitelisted } =
PalletStorageAttrInfo::from_attrs(attrs)?;
+ // set all storages to be unbounded if dev_mode is enabled
+ unbounded |= dev_mode;
let cfg_attrs = helper::get_item_cfg_attrs(&item.attrs);
let instances = vec![helper::check_type_def_gen(&item.generics, item.ident.span())?];
diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs
index 9b0ee84c34d8a..84e416e50544d 100644
--- a/frame/support/src/lib.rs
+++ b/frame/support/src/lib.rs
@@ -1487,6 +1487,36 @@ pub mod pallet_prelude {
/// non-instantiable pallets. For an example of an instantiable pallet, see [this
/// example](#example-of-an-instantiable-pallet).
///
+/// # Dev Mode (`#[pallet(dev_mode)]`)
+///
+/// Specifying the argument `dev_mode` on the `#[pallet]` or `#[frame_support::pallet]`
+/// attribute attached to your pallet module will allow you to enable dev mode for a pallet.
+/// The aim of dev mode is to loosen some of the restrictions and requirements placed on
+/// production pallets for easy tinkering and development. Dev mode pallets should not be used
+/// in production. Enabling dev mode has the following effects:
+///
+/// * Weights no longer need to be specified on every `#[pallet::call]` declaration. By
+/// default, dev mode pallets will assume a weight of zero (`0`) if a weight is not
+/// specified. This is equivalent to specifying `#[weight(0)]` on all calls that do not
+/// specify a weight.
+/// * All storages are marked as unbounded, meaning you do not need to implement
+/// `MaxEncodedLen` on storage types. This is equivalent to specifying `#[pallet::unbounded]`
+/// on all storage type definitions.
+///
+/// Note that the `dev_mode` argument can only be supplied to the `#[pallet]` or
+/// `#[frame_support::pallet]` attribute macro that encloses your pallet module. This argument
+/// cannot be specified anywhere else, including but not limited to the `#[pallet::pallet]`
+/// attribute macro.
+///
+///
+/// WARNING:
+/// You should not deploy or use dev mode pallets in production. Doing so can break your chain
+/// and therefore should never be done. Once you are done tinkering, you should remove the
+/// 'dev_mode' argument from your #[pallet] declaration and fix any compile errors before
+/// attempting to use your pallet in a production scenario.
+///
+///
/// # Pallet struct placeholder: `#[pallet::pallet]` (mandatory)
///
/// The pallet struct placeholder `#[pallet::pallet]` is mandatory and allows you to specify
diff --git a/frame/support/test/tests/pallet_ui/attr_non_empty.stderr b/frame/support/test/tests/pallet_ui/attr_non_empty.stderr
index 144af5a17ea5c..9eac5de35db80 100644
--- a/frame/support/test/tests/pallet_ui/attr_non_empty.stderr
+++ b/frame/support/test/tests/pallet_ui/attr_non_empty.stderr
@@ -1,5 +1,5 @@
-error: Invalid pallet macro call: expected no attributes, e.g. macro call must be just `#[frame_support::pallet]` or `#[pallet]`
- --> $DIR/attr_non_empty.rs:1:26
+error: Invalid pallet macro call: unexpected attribute. Macro call must be bare, such as `#[frame_support::pallet]` or `#[pallet]`, or must specify the `dev_mode` attribute, such as `#[frame_support::pallet(dev_mode)]` or #[pallet(dev_mode)].
+ --> tests/pallet_ui/attr_non_empty.rs:1:26
|
1 | #[frame_support::pallet [foo]]
| ^^^
diff --git a/frame/support/test/tests/pallet_ui/dev_mode_without_arg.rs b/frame/support/test/tests/pallet_ui/dev_mode_without_arg.rs
new file mode 100644
index 0000000000000..f044ae6d7878f
--- /dev/null
+++ b/frame/support/test/tests/pallet_ui/dev_mode_without_arg.rs
@@ -0,0 +1,33 @@
+#![cfg_attr(not(feature = "std"), no_std)]
+
+pub use pallet::*;
+
+#[frame_support::pallet]
+pub mod pallet {
+ use frame_support::pallet_prelude::*;
+ use frame_system::pallet_prelude::*;
+
+ // The struct on which we build all of our Pallet logic.
+ #[pallet::pallet]
+ pub struct Pallet(_);
+
+ // Your Pallet's configuration trait, representing custom external types and interfaces.
+ #[pallet::config]
+ pub trait Config: frame_system::Config {}
+
+ #[pallet::storage]
+ type MyStorage = StorageValue<_, Vec>;
+
+ // Your Pallet's callable functions.
+ #[pallet::call]
+ impl Pallet {
+ pub fn my_call(_origin: OriginFor) -> DispatchResult {
+ Ok(())
+ }
+ }
+
+ // Your Pallet's internal functions.
+ impl Pallet {}
+}
+
+fn main() {}
diff --git a/frame/support/test/tests/pallet_ui/dev_mode_without_arg.stderr b/frame/support/test/tests/pallet_ui/dev_mode_without_arg.stderr
new file mode 100644
index 0000000000000..fac7fd77df9ae
--- /dev/null
+++ b/frame/support/test/tests/pallet_ui/dev_mode_without_arg.stderr
@@ -0,0 +1,11 @@
+error: Invalid pallet::call, requires weight attribute i.e. `#[pallet::weight($expr)]`
+ --> tests/pallet_ui/dev_mode_without_arg.rs:24:7
+ |
+24 | pub fn my_call(_origin: OriginFor) -> DispatchResult {
+ | ^^
+
+error[E0432]: unresolved import `pallet`
+ --> tests/pallet_ui/dev_mode_without_arg.rs:3:9
+ |
+3 | pub use pallet::*;
+ | ^^^^^^ help: a similar path exists: `test_pallet::pallet`
diff --git a/frame/support/test/tests/pallet_ui/dev_mode_without_arg_max_encoded_len.rs b/frame/support/test/tests/pallet_ui/dev_mode_without_arg_max_encoded_len.rs
new file mode 100644
index 0000000000000..f6efcc3fc3d72
--- /dev/null
+++ b/frame/support/test/tests/pallet_ui/dev_mode_without_arg_max_encoded_len.rs
@@ -0,0 +1,34 @@
+#![cfg_attr(not(feature = "std"), no_std)]
+
+pub use pallet::*;
+
+#[frame_support::pallet]
+pub mod pallet {
+ use frame_support::pallet_prelude::*;
+ use frame_system::pallet_prelude::*;
+
+ // The struct on which we build all of our Pallet logic.
+ #[pallet::pallet]
+ pub struct Pallet(_);
+
+ // Your Pallet's configuration trait, representing custom external types and interfaces.
+ #[pallet::config]
+ pub trait Config: frame_system::Config {}
+
+ #[pallet::storage]
+ type MyStorage = StorageValue<_, Vec>;
+
+ // Your Pallet's callable functions.
+ #[pallet::call]
+ impl Pallet {
+ #[pallet::weight(0)]
+ pub fn my_call(_origin: OriginFor) -> DispatchResult {
+ Ok(())
+ }
+ }
+
+ // Your Pallet's internal functions.
+ impl Pallet {}
+}
+
+fn main() {}
diff --git a/frame/support/test/tests/pallet_ui/dev_mode_without_arg_max_encoded_len.stderr b/frame/support/test/tests/pallet_ui/dev_mode_without_arg_max_encoded_len.stderr
new file mode 100644
index 0000000000000..2f8d0cc761ab2
--- /dev/null
+++ b/frame/support/test/tests/pallet_ui/dev_mode_without_arg_max_encoded_len.stderr
@@ -0,0 +1,17 @@
+error[E0277]: the trait bound `Vec: MaxEncodedLen` is not satisfied
+ --> tests/pallet_ui/dev_mode_without_arg_max_encoded_len.rs:11:12
+ |
+11 | #[pallet::pallet]
+ | ^^^^^^ the trait `MaxEncodedLen` is not implemented for `Vec`
+ |
+ = help: the following other types implement trait `MaxEncodedLen`:
+ ()
+ (TupleElement0, TupleElement1)
+ (TupleElement0, TupleElement1, TupleElement2)
+ (TupleElement0, TupleElement1, TupleElement2, TupleElement3)
+ (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4)
+ (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5)
+ (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6)
+ (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6, TupleElement7)
+ and 78 others
+ = note: required because of the requirements on the impl of `StorageInfoTrait` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageMyStorage, Vec>`
diff --git a/frame/support/test/tests/pallet_ui/pallet_invalid_arg.rs b/frame/support/test/tests/pallet_ui/pallet_invalid_arg.rs
new file mode 100644
index 0000000000000..1fc42f6511cfa
--- /dev/null
+++ b/frame/support/test/tests/pallet_ui/pallet_invalid_arg.rs
@@ -0,0 +1,4 @@
+#[frame_support::pallet(foo)]
+pub mod pallet {}
+
+fn main() {}
diff --git a/frame/support/test/tests/pallet_ui/pallet_invalid_arg.stderr b/frame/support/test/tests/pallet_ui/pallet_invalid_arg.stderr
new file mode 100644
index 0000000000000..234dc07f2ece3
--- /dev/null
+++ b/frame/support/test/tests/pallet_ui/pallet_invalid_arg.stderr
@@ -0,0 +1,5 @@
+error: Invalid pallet macro call: unexpected attribute. Macro call must be bare, such as `#[frame_support::pallet]` or `#[pallet]`, or must specify the `dev_mode` attribute, such as `#[frame_support::pallet(dev_mode)]` or #[pallet(dev_mode)].
+ --> tests/pallet_ui/pallet_invalid_arg.rs:1:25
+ |
+1 | #[frame_support::pallet(foo)]
+ | ^^^
diff --git a/frame/support/test/tests/pallet_ui/pass/dev_mode_valid.rs b/frame/support/test/tests/pallet_ui/pass/dev_mode_valid.rs
new file mode 100644
index 0000000000000..97e0d585d0ce9
--- /dev/null
+++ b/frame/support/test/tests/pallet_ui/pass/dev_mode_valid.rs
@@ -0,0 +1,35 @@
+#![cfg_attr(not(feature = "std"), no_std)]
+
+pub use pallet::*;
+
+#[frame_support::pallet(dev_mode)]
+pub mod pallet {
+ use frame_support::pallet_prelude::*;
+ use frame_system::pallet_prelude::*;
+
+ // The struct on which we build all of our Pallet logic.
+ #[pallet::pallet]
+ pub struct Pallet(_);
+
+ // Your Pallet's configuration trait, representing custom external types and interfaces.
+ #[pallet::config]
+ pub trait Config: frame_system::Config {}
+
+ // The MEL requirement for bounded pallets is skipped by `dev_mode`.
+ #[pallet::storage]
+ type MyStorage = StorageValue<_, Vec>;
+
+ // Your Pallet's callable functions.
+ #[pallet::call]
+ impl Pallet {
+ // No need to define a `weight` attribute here because of `dev_mode`.
+ pub fn my_call(_origin: OriginFor) -> DispatchResult {
+ Ok(())
+ }
+ }
+
+ // Your Pallet's internal functions.
+ impl Pallet {}
+}
+
+fn main() {}