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