mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-30 23:37:56 +00:00
Adds syntax for marking calls feeless (#1926)
Fixes https://github.com/paritytech/polkadot-sdk/issues/1725 This PR adds the following changes: 1. An attribute `pallet::feeless_if` that can be optionally attached to a call like so: ```rust #[pallet::feeless_if(|_origin: &OriginFor<T>, something: &u32| -> bool { *something == 0 })] pub fn do_something(origin: OriginFor<T>, something: u32) -> DispatchResult { .... } ``` The closure passed accepts references to arguments as specified in the call fn. It returns a boolean that denotes the conditions required for this call to be "feeless". 2. A signed extension `SkipCheckIfFeeless<T: SignedExtension>` that wraps a transaction payment processor such as `pallet_transaction_payment::ChargeTransactionPayment`. It checks for all calls annotated with `pallet::feeless_if` to see if the conditions are met. If so, the wrapped signed extension is not called, essentially making the call feeless. In order to use this, you can simply replace your existing signed extension that manages transaction payment like so: ```diff - pallet_transaction_payment::ChargeTransactionPayment<Runtime>, + pallet_skip_feeless_payment::SkipCheckIfFeeless< + Runtime, + pallet_transaction_payment::ChargeTransactionPayment<Runtime>, + >, ``` ### Todo - [x] Tests - [x] Docs - [x] Prdoc --------- Co-authored-by: Nikhil Gupta <> Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com> Co-authored-by: Liam Aharon <liam.aharon@hotmail.com>
This commit is contained in:
@@ -124,6 +124,18 @@ pub fn expand_outer_dispatch(
|
||||
}
|
||||
}
|
||||
|
||||
impl #scrate::dispatch::CheckIfFeeless for RuntimeCall {
|
||||
type Origin = #system_path::pallet_prelude::OriginFor<#runtime>;
|
||||
fn is_feeless(&self, origin: &Self::Origin) -> bool {
|
||||
match self {
|
||||
#(
|
||||
#pallet_attrs
|
||||
#variant_patterns => call.is_feeless(origin),
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl #scrate::traits::GetCallMetadata for RuntimeCall {
|
||||
fn get_call_metadata(&self) -> #scrate::traits::CallMetadata {
|
||||
use #scrate::traits::GetCallName;
|
||||
|
||||
@@ -1157,6 +1157,36 @@ pub fn call_index(_: TokenStream, _: TokenStream) -> TokenStream {
|
||||
pallet_macro_stub()
|
||||
}
|
||||
|
||||
/// Each dispatchable may be annotated with the `#[pallet::feeless_if($closure)]` attribute,
|
||||
/// which explicitly defines the condition for the dispatchable to be feeless.
|
||||
///
|
||||
/// The arguments for the closure must be the referenced arguments of the dispatchable function.
|
||||
///
|
||||
/// The closure must return `bool`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// #[pallet::feeless_if(|_origin: &OriginFor<T>, something: &u32| -> bool {
|
||||
/// *something == 0
|
||||
/// })]
|
||||
/// pub fn do_something(origin: OriginFor<T>, something: u32) -> DispatchResult {
|
||||
/// ....
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Please note that this only works for signed dispatchables and requires a signed extension
|
||||
/// such as `SkipCheckIfFeeless` as defined in `pallet-skip-feeless-payment` to wrap the existing
|
||||
/// payment extension. Else, this is completely ignored and the dispatchable is still charged.
|
||||
///
|
||||
/// ### Macro expansion
|
||||
///
|
||||
/// The macro implements the `CheckIfFeeless` trait on the dispatchable and calls the corresponding
|
||||
/// closure in the implementation.
|
||||
#[proc_macro_attribute]
|
||||
pub fn feeless_if(_: TokenStream, _: TokenStream) -> TokenStream {
|
||||
pallet_macro_stub()
|
||||
}
|
||||
|
||||
/// Allows you to define some extra constants to be added into constant metadata.
|
||||
///
|
||||
/// Item must be defined as:
|
||||
|
||||
@@ -241,6 +241,16 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let feeless_check = methods.iter().map(|method| &method.feeless_check).collect::<Vec<_>>();
|
||||
let feeless_check_result =
|
||||
feeless_check.iter().zip(args_name.iter()).map(|(feeless_check, arg_name)| {
|
||||
if let Some(feeless_check) = feeless_check {
|
||||
quote::quote!(#feeless_check(origin, #( #arg_name, )*))
|
||||
} else {
|
||||
quote::quote!(false)
|
||||
}
|
||||
});
|
||||
|
||||
quote::quote_spanned!(span =>
|
||||
mod warnings {
|
||||
#(
|
||||
@@ -347,6 +357,23 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
}
|
||||
}
|
||||
|
||||
impl<#type_impl_gen> #frame_support::dispatch::CheckIfFeeless for #call_ident<#type_use_gen>
|
||||
#where_clause
|
||||
{
|
||||
type Origin = #frame_system::pallet_prelude::OriginFor<T>;
|
||||
#[allow(unused_variables)]
|
||||
fn is_feeless(&self, origin: &Self::Origin) -> bool {
|
||||
match *self {
|
||||
#(
|
||||
Self::#fn_name { #( #args_name_pattern_ref, )* } => {
|
||||
#feeless_check_result
|
||||
},
|
||||
)*
|
||||
Self::__Ignore(_, _) => unreachable!("__Ignore cannot be used"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<#type_impl_gen> #frame_support::traits::GetCallName for #call_ident<#type_use_gen>
|
||||
#where_clause
|
||||
{
|
||||
|
||||
@@ -17,9 +17,10 @@
|
||||
|
||||
use super::{helper, InheritedCallWeightAttr};
|
||||
use frame_support_procedural_tools::get_doc_literals;
|
||||
use proc_macro2::Span;
|
||||
use quote::ToTokens;
|
||||
use std::collections::HashMap;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{spanned::Spanned, ExprClosure};
|
||||
|
||||
/// List of additional token to be used for parsing.
|
||||
mod keyword {
|
||||
@@ -30,6 +31,7 @@ mod keyword {
|
||||
syn::custom_keyword!(compact);
|
||||
syn::custom_keyword!(T);
|
||||
syn::custom_keyword!(pallet);
|
||||
syn::custom_keyword!(feeless_if);
|
||||
}
|
||||
|
||||
/// Definition of dispatchables typically `impl<T: Config> Pallet<T> { ... }`
|
||||
@@ -82,13 +84,18 @@ pub struct CallVariantDef {
|
||||
pub docs: Vec<syn::Expr>,
|
||||
/// Attributes annotated at the top of the dispatchable function.
|
||||
pub attrs: Vec<syn::Attribute>,
|
||||
/// The optional `feeless_if` attribute on the `pallet::call`.
|
||||
pub feeless_check: Option<syn::ExprClosure>,
|
||||
}
|
||||
|
||||
/// Attributes for functions in call impl block.
|
||||
/// Parse for `#[pallet::weight(expr)]` or `#[pallet::call_index(expr)]
|
||||
pub enum FunctionAttr {
|
||||
/// Parse for `#[pallet::call_index(expr)]`
|
||||
CallIndex(u8),
|
||||
/// Parse for `#[pallet::weight(expr)]`
|
||||
Weight(syn::Expr),
|
||||
/// Parse for `#[pallet::feeless_if(expr)]`
|
||||
FeelessIf(Span, syn::ExprClosure),
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for FunctionAttr {
|
||||
@@ -115,6 +122,19 @@ impl syn::parse::Parse for FunctionAttr {
|
||||
return Err(syn::Error::new(index.span(), msg))
|
||||
}
|
||||
Ok(FunctionAttr::CallIndex(index.base10_parse()?))
|
||||
} else if lookahead.peek(keyword::feeless_if) {
|
||||
content.parse::<keyword::feeless_if>()?;
|
||||
let closure_content;
|
||||
syn::parenthesized!(closure_content in content);
|
||||
Ok(FunctionAttr::FeelessIf(
|
||||
closure_content.span(),
|
||||
closure_content.parse::<syn::ExprClosure>().map_err(|e| {
|
||||
let msg = "Invalid feeless_if attribute: expected a closure";
|
||||
let mut err = syn::Error::new(closure_content.span(), msg);
|
||||
err.combine(e);
|
||||
err
|
||||
})?,
|
||||
))
|
||||
} else {
|
||||
Err(lookahead.error())
|
||||
}
|
||||
@@ -138,28 +158,33 @@ impl syn::parse::Parse for ArgAttrIsCompact {
|
||||
}
|
||||
}
|
||||
|
||||
/// Check the syntax is `OriginFor<T>`
|
||||
pub fn check_dispatchable_first_arg_type(ty: &syn::Type) -> syn::Result<()> {
|
||||
pub struct CheckDispatchableFirstArg;
|
||||
/// Check the syntax is `OriginFor<T>` or `&OriginFor<T>`.
|
||||
pub fn check_dispatchable_first_arg_type(ty: &syn::Type, is_ref: bool) -> syn::Result<()> {
|
||||
pub struct CheckDispatchableFirstArg(bool);
|
||||
impl syn::parse::Parse for CheckDispatchableFirstArg {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let is_ref = input.parse::<syn::Token![&]>().is_ok();
|
||||
input.parse::<keyword::OriginFor>()?;
|
||||
input.parse::<syn::Token![<]>()?;
|
||||
input.parse::<keyword::T>()?;
|
||||
input.parse::<syn::Token![>]>()?;
|
||||
|
||||
Ok(Self)
|
||||
Ok(Self(is_ref))
|
||||
}
|
||||
}
|
||||
|
||||
syn::parse2::<CheckDispatchableFirstArg>(ty.to_token_stream()).map_err(|e| {
|
||||
let msg = "Invalid type: expected `OriginFor<T>`";
|
||||
let mut err = syn::Error::new(ty.span(), msg);
|
||||
err.combine(e);
|
||||
err
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
let result = syn::parse2::<CheckDispatchableFirstArg>(ty.to_token_stream());
|
||||
return match result {
|
||||
Ok(CheckDispatchableFirstArg(has_ref)) if is_ref == has_ref => Ok(()),
|
||||
_ => {
|
||||
let msg = if is_ref {
|
||||
"Invalid type: expected `&OriginFor<T>`"
|
||||
} else {
|
||||
"Invalid type: expected `OriginFor<T>`"
|
||||
};
|
||||
return Err(syn::Error::new(ty.span(), msg))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
impl CallDef {
|
||||
@@ -215,7 +240,7 @@ impl CallDef {
|
||||
return Err(syn::Error::new(method.sig.span(), msg))
|
||||
},
|
||||
Some(syn::FnArg::Typed(arg)) => {
|
||||
check_dispatchable_first_arg_type(&arg.ty)?;
|
||||
check_dispatchable_first_arg_type(&arg.ty, false)?;
|
||||
},
|
||||
}
|
||||
|
||||
@@ -227,16 +252,22 @@ impl CallDef {
|
||||
return Err(syn::Error::new(method.sig.span(), msg))
|
||||
}
|
||||
|
||||
let (mut weight_attrs, mut call_idx_attrs): (Vec<FunctionAttr>, Vec<FunctionAttr>) =
|
||||
helper::take_item_pallet_attrs(&mut method.attrs)?.into_iter().partition(
|
||||
|attr| {
|
||||
if let FunctionAttr::Weight(_) = attr {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
let mut call_idx_attrs = vec![];
|
||||
let mut weight_attrs = vec![];
|
||||
let mut feeless_attrs = vec![];
|
||||
for attr in helper::take_item_pallet_attrs(&mut method.attrs)?.into_iter() {
|
||||
match attr {
|
||||
FunctionAttr::CallIndex(_) => {
|
||||
call_idx_attrs.push(attr);
|
||||
},
|
||||
);
|
||||
FunctionAttr::Weight(_) => {
|
||||
weight_attrs.push(attr);
|
||||
},
|
||||
FunctionAttr::FeelessIf(span, _) => {
|
||||
feeless_attrs.push((span, attr));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if weight_attrs.is_empty() && dev_mode {
|
||||
// inject a default O(1) weight when dev mode is enabled and no weight has
|
||||
@@ -323,6 +354,73 @@ impl CallDef {
|
||||
|
||||
let docs = get_doc_literals(&method.attrs);
|
||||
|
||||
if feeless_attrs.len() > 1 {
|
||||
let msg = "Invalid pallet::call, there can only be one feeless_if attribute";
|
||||
return Err(syn::Error::new(feeless_attrs[1].0, msg))
|
||||
}
|
||||
let feeless_check: Option<ExprClosure> =
|
||||
feeless_attrs.pop().map(|(_, attr)| match attr {
|
||||
FunctionAttr::FeelessIf(_, closure) => closure,
|
||||
_ => unreachable!("checked during creation of the let binding"),
|
||||
});
|
||||
|
||||
if let Some(ref feeless_check) = feeless_check {
|
||||
if feeless_check.inputs.len() != args.len() + 1 {
|
||||
let msg = "Invalid pallet::call, feeless_if closure must have same \
|
||||
number of arguments as the dispatchable function";
|
||||
return Err(syn::Error::new(feeless_check.span(), msg))
|
||||
}
|
||||
|
||||
match feeless_check.inputs.first() {
|
||||
None => {
|
||||
let msg = "Invalid pallet::call, feeless_if closure must have at least origin arg";
|
||||
return Err(syn::Error::new(feeless_check.span(), msg))
|
||||
},
|
||||
Some(syn::Pat::Type(arg)) => {
|
||||
check_dispatchable_first_arg_type(&arg.ty, true)?;
|
||||
},
|
||||
_ => {
|
||||
let msg = "Invalid pallet::call, feeless_if closure first argument must be a typed argument, \
|
||||
e.g. `origin: OriginFor<T>`";
|
||||
return Err(syn::Error::new(feeless_check.span(), msg))
|
||||
},
|
||||
}
|
||||
|
||||
for (feeless_arg, arg) in feeless_check.inputs.iter().skip(1).zip(args.iter()) {
|
||||
let feeless_arg_type =
|
||||
if let syn::Pat::Type(syn::PatType { ty, .. }) = feeless_arg.clone() {
|
||||
if let syn::Type::Reference(pat) = *ty {
|
||||
pat.elem.clone()
|
||||
} else {
|
||||
let msg = "Invalid pallet::call, feeless_if closure argument must be a reference";
|
||||
return Err(syn::Error::new(ty.span(), msg))
|
||||
}
|
||||
} else {
|
||||
let msg = "Invalid pallet::call, feeless_if closure argument must be a type ascription pattern";
|
||||
return Err(syn::Error::new(feeless_arg.span(), msg))
|
||||
};
|
||||
|
||||
if feeless_arg_type != arg.2 {
|
||||
let msg =
|
||||
"Invalid pallet::call, feeless_if closure argument must have \
|
||||
a reference to the same type as the dispatchable function argument";
|
||||
return Err(syn::Error::new(feeless_arg.span(), msg))
|
||||
}
|
||||
}
|
||||
|
||||
let valid_return = match &feeless_check.output {
|
||||
syn::ReturnType::Type(_, type_) => match *(type_.clone()) {
|
||||
syn::Type::Path(syn::TypePath { path, .. }) => path.is_ident("bool"),
|
||||
_ => false,
|
||||
},
|
||||
_ => false,
|
||||
};
|
||||
if !valid_return {
|
||||
let msg = "Invalid pallet::call, feeless_if closure must return `bool`";
|
||||
return Err(syn::Error::new(feeless_check.output.span(), msg))
|
||||
}
|
||||
}
|
||||
|
||||
methods.push(CallVariantDef {
|
||||
name: method.sig.ident.clone(),
|
||||
weight,
|
||||
@@ -331,6 +429,7 @@ impl CallDef {
|
||||
args,
|
||||
docs,
|
||||
attrs: method.attrs.clone(),
|
||||
feeless_check,
|
||||
});
|
||||
} else {
|
||||
let msg = "Invalid pallet::call, only method accepted";
|
||||
|
||||
@@ -54,6 +54,20 @@ pub trait Callable<T> {
|
||||
// https://github.com/rust-lang/rust/issues/51331
|
||||
pub type CallableCallFor<A, R> = <A as Callable<R>>::RuntimeCall;
|
||||
|
||||
/// Means to checks if the dispatchable is feeless.
|
||||
///
|
||||
/// This is automatically implemented for all dispatchables during pallet expansion.
|
||||
/// If a call is marked by [`#[pallet::feeless_if]`](`macro@frame_support_procedural::feeless_if`)
|
||||
/// attribute, the corresponding closure is checked.
|
||||
pub trait CheckIfFeeless {
|
||||
/// The Origin type of the runtime.
|
||||
type Origin;
|
||||
|
||||
/// Checks if the dispatchable satisfies the feeless condition as defined by
|
||||
/// [`#[pallet::feeless_if]`](`macro@frame_support_procedural::feeless_if`)
|
||||
fn is_feeless(&self, origin: &Self::Origin) -> bool;
|
||||
}
|
||||
|
||||
/// Origin for the System pallet.
|
||||
#[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)]
|
||||
pub enum RawOrigin<AccountId> {
|
||||
|
||||
@@ -2227,9 +2227,10 @@ pub use frame_support_procedural::pallet;
|
||||
pub mod pallet_macros {
|
||||
pub use frame_support_procedural::{
|
||||
call_index, compact, composite_enum, config, disable_frame_system_supertrait_check, error,
|
||||
event, extra_constants, generate_deposit, generate_store, getter, hooks, import_section,
|
||||
inherent, no_default, no_default_bounds, origin, pallet_section, storage_prefix,
|
||||
storage_version, type_value, unbounded, validate_unsigned, weight, whitelist_storage,
|
||||
event, extra_constants, feeless_if, generate_deposit, generate_store, getter, hooks,
|
||||
import_section, inherent, no_default, no_default_bounds, origin, pallet_section,
|
||||
storage_prefix, storage_version, type_value, unbounded, validate_unsigned, weight,
|
||||
whitelist_storage,
|
||||
};
|
||||
|
||||
/// Allows you to define the genesis configuration for the pallet.
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#[frame_support::pallet(dev_mode)]
|
||||
mod pallet {
|
||||
use frame_support::pallet_prelude::DispatchResult;
|
||||
use frame_system::pallet_prelude::OriginFor;
|
||||
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {}
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(core::marker::PhantomData<T>);
|
||||
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
#[pallet::feeless_if(|| -> bool { true })]
|
||||
pub fn foo(_: OriginFor<T>) -> DispatchResult { Ok(()) }
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: Invalid pallet::call, feeless_if closure must have same number of arguments as the dispatchable function
|
||||
--> tests/pallet_ui/call_feeless_invalid_closure_arg1.rs:31:24
|
||||
|
|
||||
31 | #[pallet::feeless_if(|| -> bool { true })]
|
||||
| ^
|
||||
@@ -0,0 +1,37 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#[frame_support::pallet(dev_mode)]
|
||||
mod pallet {
|
||||
use frame_support::pallet_prelude::DispatchResult;
|
||||
use frame_system::pallet_prelude::OriginFor;
|
||||
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {}
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(core::marker::PhantomData<T>);
|
||||
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
#[pallet::feeless_if(|_: bool| -> bool { true })]
|
||||
pub fn foo(_: OriginFor<T>) -> DispatchResult { Ok(()) }
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: Invalid type: expected `&OriginFor<T>`
|
||||
--> tests/pallet_ui/call_feeless_invalid_closure_arg2.rs:31:28
|
||||
|
|
||||
31 | #[pallet::feeless_if(|_: bool| -> bool { true })]
|
||||
| ^^^^
|
||||
@@ -0,0 +1,37 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#[frame_support::pallet(dev_mode)]
|
||||
mod pallet {
|
||||
use frame_support::pallet_prelude::DispatchResult;
|
||||
use frame_system::pallet_prelude::OriginFor;
|
||||
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {}
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(core::marker::PhantomData<T>);
|
||||
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
#[pallet::feeless_if(|_: &OriginFor<T>, _s: &u32| -> bool { true })]
|
||||
pub fn foo(_: OriginFor<T>, _something: u64) -> DispatchResult { Ok(()) }
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: Invalid pallet::call, feeless_if closure argument must have a reference to the same type as the dispatchable function argument
|
||||
--> tests/pallet_ui/call_feeless_invalid_closure_arg3.rs:31:43
|
||||
|
|
||||
31 | #[pallet::feeless_if(|_: &OriginFor<T>, _s: &u32| -> bool { true })]
|
||||
| ^^
|
||||
@@ -0,0 +1,37 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#[frame_support::pallet(dev_mode)]
|
||||
mod pallet {
|
||||
use frame_support::pallet_prelude::DispatchResult;
|
||||
use frame_system::pallet_prelude::OriginFor;
|
||||
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {}
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(core::marker::PhantomData<T>);
|
||||
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
#[pallet::feeless_if(|_: &OriginFor<T>| -> u32 { 0 })]
|
||||
pub fn foo(_: OriginFor<T>) -> DispatchResult { Ok(()) }
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
error: Invalid pallet::call, feeless_if closure must return `bool`
|
||||
--> tests/pallet_ui/call_feeless_invalid_closure_return.rs:31:43
|
||||
|
|
||||
31 | #[pallet::feeless_if(|_: &OriginFor<T>| -> u32 { 0 })]
|
||||
| ^
|
||||
@@ -0,0 +1,37 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#[frame_support::pallet(dev_mode)]
|
||||
mod pallet {
|
||||
use frame_support::pallet_prelude::DispatchResult;
|
||||
use frame_system::pallet_prelude::OriginFor;
|
||||
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {}
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(core::marker::PhantomData<T>);
|
||||
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
#[pallet::feeless_if(0)]
|
||||
pub fn foo(_: OriginFor<T>) -> DispatchResult { Ok(()) }
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
error: Invalid feeless_if attribute: expected a closure
|
||||
--> tests/pallet_ui/call_feeless_invalid_type.rs:31:24
|
||||
|
|
||||
31 | #[pallet::feeless_if(0)]
|
||||
| ^
|
||||
|
||||
error: expected `|`
|
||||
--> tests/pallet_ui/call_feeless_invalid_type.rs:31:24
|
||||
|
|
||||
31 | #[pallet::feeless_if(0)]
|
||||
| ^
|
||||
@@ -1,4 +1,4 @@
|
||||
error: expected `weight` or `call_index`
|
||||
error: expected one of: `weight`, `call_index`, `feeless_if`
|
||||
--> tests/pallet_ui/call_invalid_attr.rs:31:13
|
||||
|
|
||||
31 | #[pallet::weird_attr]
|
||||
|
||||
@@ -3,9 +3,3 @@ error: Invalid type: expected `OriginFor<T>`
|
||||
|
|
||||
34 | pub fn foo(origin: u8) {}
|
||||
| ^^
|
||||
|
||||
error: expected `OriginFor`
|
||||
--> tests/pallet_ui/call_invalid_origin_type.rs:34:22
|
||||
|
|
||||
34 | pub fn foo(origin: u8) {}
|
||||
| ^^
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#[frame_support::pallet(dev_mode)]
|
||||
mod pallet {
|
||||
use frame_support::pallet_prelude::DispatchResult;
|
||||
use frame_system::pallet_prelude::OriginFor;
|
||||
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {}
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(core::marker::PhantomData<T>);
|
||||
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
#[pallet::feeless_if(|_: &OriginFor<T>| -> bool { true })]
|
||||
pub fn foo(_: OriginFor<T>) -> DispatchResult { Ok(()) }
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
||||
Reference in New Issue
Block a user