subspace_runtime_primitives/
utility.rs

1//! Runtime primitives for pallet-utility.
2
3use core::marker::PhantomData;
4use frame_support::pallet_prelude::TypeInfo;
5use frame_system::pallet_prelude::RuntimeCallFor;
6use scale_info::prelude::collections::VecDeque;
7use scale_info::prelude::vec;
8use sp_runtime::traits::{BlockNumberProvider, Get};
9use sp_runtime::Vec;
10
11/// Trait used to convert from a generated `RuntimeCall` type to `pallet_utility::Call<Runtime>`.
12pub trait MaybeUtilityCall<Runtime>
13where
14    Runtime: pallet_utility::Config,
15    for<'call> &'call RuntimeCallFor<Runtime>:
16        From<&'call <Runtime as pallet_utility::Config>::RuntimeCall>,
17{
18    /// If this call is a `pallet_utility::Call<Runtime>` call, returns the inner `pallet_utility::Call`.
19    fn maybe_utility_call(&self) -> Option<&pallet_utility::Call<Runtime>>;
20
21    /// If this call is a `pallet_utility::Call<Runtime>` call, returns the inner `RuntimeCall`.
22    ///
23    /// Runtimes can override this default implementation if they want to ignore (or not ignore)
24    /// certain utility calls. For example, a stack limit check shouldn't ignore `Call::__Ignore`.
25    fn maybe_nested_utility_calls(&self) -> Option<Vec<&RuntimeCallFor<Runtime>>> {
26        if let Some(call) = self.maybe_utility_call() {
27            match call {
28                pallet_utility::Call::batch { calls }
29                | pallet_utility::Call::batch_all { calls }
30                | pallet_utility::Call::force_batch { calls } => {
31                    Some(calls.iter().map(Into::into).collect())
32                }
33                pallet_utility::Call::as_derivative { call, .. }
34                | pallet_utility::Call::dispatch_as { call, .. }
35                | pallet_utility::Call::with_weight { call, .. } => {
36                    Some(vec![call.as_ref().into()])
37                }
38                pallet_utility::Call::__Ignore(..) => None,
39            }
40        } else {
41            None
42        }
43    }
44}
45
46/// Trait used to convert from a generated `RuntimeCall` type to `pallet_multisig::Call<Runtime>`.
47pub trait MaybeMultisigCall<Runtime>
48where
49    Runtime: pallet_multisig::Config,
50    for<'call> &'call RuntimeCallFor<Runtime>:
51        From<&'call <Runtime as pallet_multisig::Config>::RuntimeCall>,
52{
53    /// If this call is a `pallet_multisig::Call<Runtime>` call, returns the inner `pallet_multisig::Call`.
54    fn maybe_multisig_call(&self) -> Option<&pallet_multisig::Call<Runtime>>;
55
56    /// If this call is a `pallet_multisig::Call<Runtime>` call, returns the inner `RuntimeCall`.
57    ///
58    /// Runtimes can override this default implementation if they want to ignore (or not ignore)
59    /// certain multisig calls.
60    fn maybe_nested_multisig_calls(&self) -> Option<Vec<&RuntimeCallFor<Runtime>>> {
61        if let Some(call) = self.maybe_multisig_call() {
62            match call {
63                pallet_multisig::Call::as_multi { call, .. }
64                | pallet_multisig::Call::as_multi_threshold_1 { call, .. } => Some(vec![call.as_ref().into()]),
65                // Doesn't contain any actual calls
66                pallet_multisig::Call::approve_as_multi {  .. }
67                | pallet_multisig::Call::cancel_as_multi { .. }
68                // Ignored calls
69                | pallet_multisig::Call::__Ignore(..) => None,
70            }
71        } else {
72            None
73        }
74    }
75}
76
77/// Trait used to extract nested `RuntimeCall`s from a `RuntimeCall` type.
78/// Each runtime has a different set of pallets which can nest calls.
79pub trait MaybeNestedCall<Runtime: frame_system::Config> {
80    /// If this call is a nested runtime call, returns the inner call(s).
81    ///
82    /// Ignored calls (such as `pallet_utility::Call::__Ignore`) should be yielded themsevles, but
83    /// their contents should not be yielded.
84    fn maybe_nested_call(&self) -> Option<Vec<&RuntimeCallFor<Runtime>>>;
85}
86
87/// Returns an interator over `call`, and any calls nested within it.
88///
89/// The iterator yields all calls in depth-first order, including calls which contain other calls.
90/// Ignored calls (such as `pallet_utility::Call::__Ignore`) are yielded themsevles, but their
91/// contents are not.
92///
93/// This function doesn't use stack recursion, so there's no need to check the recursion depth.
94pub fn nested_call_iter<Runtime>(
95    call: &RuntimeCallFor<Runtime>,
96) -> impl Iterator<Item = &RuntimeCallFor<Runtime>>
97where
98    Runtime: frame_system::Config,
99    RuntimeCallFor<Runtime>: MaybeNestedCall<Runtime>,
100{
101    // Instead of using recursion, we allocate references to each call on the heap.
102    let mut new_calls = VecDeque::from([call]);
103
104    core::iter::from_fn(move || {
105        let call = new_calls.pop_front()?;
106
107        for call in call.maybe_nested_call().into_iter().flatten() {
108            new_calls.push_front(call);
109        }
110
111        Some(call)
112    })
113}
114
115// `DefaultNonceProvider` uses the current block number as the nonce of the new account,
116// this is used to prevent the replay attack see https://wiki.polkadot.network/docs/transaction-attacks#replay-attack
117// for more detail.
118#[derive(Debug, TypeInfo)]
119pub struct DefaultNonceProvider<T, N>(PhantomData<(T, N)>);
120
121impl<N, T: BlockNumberProvider<BlockNumber = N>> Get<N> for DefaultNonceProvider<T, N> {
122    fn get() -> N {
123        T::current_block_number()
124    }
125}