pallet_collective_proxy/
lib.rs

1// This file is part of Astar.
2
3// Copyright (C) Stake Technologies Pte.Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later
5
6// Astar is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// Astar is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with Astar. If not, see <http://www.gnu.org/licenses/>.
18
19#![cfg_attr(not(feature = "std"), no_std)]
20
21use frame_support::{
22    dispatch::GetDispatchInfo,
23    pallet_prelude::*,
24    traits::{InstanceFilter, IsType, OriginTrait},
25};
26use frame_system::pallet_prelude::*;
27use sp_runtime::traits::Dispatchable;
28use sp_std::prelude::*;
29
30pub use pallet::*;
31
32#[cfg(test)]
33mod mock;
34#[cfg(test)]
35mod tests;
36
37#[cfg(feature = "runtime-benchmarks")]
38mod benchmarking;
39
40pub mod weights;
41pub use weights::WeightInfo;
42
43#[frame_support::pallet]
44pub mod pallet {
45    use super::*;
46
47    #[pallet::pallet]
48    pub struct Pallet<T>(_);
49
50    // TODO: The pallet is intentionally very basic. It could be improved to handle more origins, more aliases, etc.
51    // There could also be different instances, if such approach was needed.
52    // However, it's supposed to be the simplest solution possible to cover a specific scenario.
53    // Pallet is stateless and can easily be upgraded in the future.
54
55    /// Configuration trait.
56    #[pallet::config]
57    pub trait Config: frame_system::Config {
58        /// The overarching call type.
59        type RuntimeCall: Parameter
60            + Dispatchable<RuntimeOrigin = Self::RuntimeOrigin>
61            + GetDispatchInfo
62            + From<frame_system::Call<Self>>
63            + IsType<<Self as frame_system::Config>::RuntimeCall>;
64
65        /// Origin that can act on behalf of the collective.
66        type CollectiveProxy: EnsureOrigin<<Self as frame_system::Config>::RuntimeOrigin>;
67
68        /// Account representing the collective treasury.
69        type ProxyAccountId: Get<Self::AccountId>;
70
71        /// Filter to determine whether a call can be executed or not.
72        type CallFilter: InstanceFilter<<Self as Config>::RuntimeCall> + Default;
73
74        /// Weight info
75        type WeightInfo: WeightInfo;
76    }
77
78    #[pallet::event]
79    #[pallet::generate_deposit(pub(super) fn deposit_event)]
80    pub enum Event<T: Config> {
81        /// Community proxy call executed successfully.
82        CollectiveProxyExecuted { result: DispatchResult },
83    }
84
85    #[pallet::call]
86    impl<T: Config> Pallet<T> {
87        /// Executes the call on a behalf of an aliased account.
88        ///
89        /// The `origin` of the call is supposed to be a _collective_ (but can be anything) which can dispatch `call` on behalf of the aliased account.
90        /// It's essentially a proxy call that can be made by arbitrary origin type.
91        #[pallet::call_index(0)]
92        #[pallet::weight({
93			let di = call.get_dispatch_info();
94			(T::WeightInfo::execute_call().saturating_add(di.total_weight()), di.class)
95		})]
96        pub fn execute_call(
97            origin: OriginFor<T>,
98            call: Box<<T as Config>::RuntimeCall>,
99        ) -> DispatchResult {
100            // Ensure origin is valid.
101            T::CollectiveProxy::ensure_origin(origin)?;
102
103            // Account authentication is ensured by the `CollectiveProxy` origin check.
104            let mut origin: T::RuntimeOrigin =
105                frame_system::RawOrigin::Signed(T::ProxyAccountId::get()).into();
106
107            // Ensure custom filter is applied.
108            origin.add_filter(move |c: &<T as frame_system::Config>::RuntimeCall| {
109                let c = <T as Config>::RuntimeCall::from_ref(c);
110                T::CallFilter::default().filter(c)
111            });
112
113            // Dispatch the call.
114            let e = call.dispatch(origin);
115            Self::deposit_event(Event::CollectiveProxyExecuted {
116                result: e.map(|_| ()).map_err(|e| e.error),
117            });
118
119            Ok(())
120        }
121    }
122}